workgroups/vendor/github.com/posener/script/stream.go
Marvin Preuss f121b05b58
Some checks failed
continuous-integration/drone/push Build is failing
repo: adds goreadme
2021-09-24 18:29:05 +02:00

102 lines
3.1 KiB
Go

// Package script provides helper functions to write scripts.
//
// Inspired by https://github.com/bitfield/script, with some improvements:
//
// * Output between streamed commands is a stream and not loaded to memory.
//
// * Better representation and handling of errors.
//
// * Proper incocation, usage and handling of stderr of custom commands.
//
// The script chain is represented by a
// (`Stream`) https://godoc.org/github.com/posener/script#Stream object. While each command in the
// stream is abstracted by the (`Command`) https://godoc.org/github.com/posener/script#Command
// struct. This library provides basic functionality, but can be extended freely.
package script
import (
"io"
"reflect"
"github.com/hashicorp/go-multierror"
)
// Stream is a chain of operations on a stream of bytes. The stdout of each operation in the stream
// feeds the following operation stdin. The stream object have different methods that allow
// manipulating it, most of them resemble well known linux commands.
//
// A custom modifier can be used with the `Through` or with the `Modify` functions.
//
// The stream object is created by some in this library or from any `io.Reader` using the `From`
// function. It can be dumped using some functions in this library, to a custom reader using the
// `To` method.
type Stream struct {
// r is the output reader of this stream. If r also implements the `io.Closer` interface, it
// will be closed when the stream is closed.
r io.Reader
// stage is the name of the current stage in the stream.
stage string
// parent points to the stage before the current stage in the stream.
parent *Stream
// err contains an error from the current stage in the stream.
err error
}
// Read can be used to read from the stream.
func (s Stream) Read(b []byte) (int, error) {
return s.r.Read(b)
}
// Close closes all the stages in the stream and return the errors that occurred in all of the
// stages.
func (s Stream) Close() error {
var errors *multierror.Error
for cur := &s; cur != nil; cur = cur.parent {
if cur.err != nil {
errors = multierror.Append(errors, cur.err)
}
if closer, ok := cur.r.(io.Closer); ok {
if err := closer.Close(); err != nil {
errors = multierror.Append(errors, err)
}
}
}
return errors.ErrorOrNil()
}
// Through passes the current stream through a pipe. This function can be used to add custom
// commands that are not available in this library.
func (s Stream) Through(pipe Pipe) Stream {
r, err := pipe.Pipe(s.r)
if r == nil {
panic("a command must contain a reader")
}
return Stream{
stage: pipe.Name(),
r: r,
err: err,
parent: &s,
}
}
// Pipe reads from a reader and returns another reader.
type Pipe interface {
// Pipe gets a reader and returns another reader. A pipe may return an error and a reader
// together.
Pipe(stdin io.Reader) (io.Reader, error)
// Name of pipe.
Name() string
}
// PipeFn is a function that implements Pipe.
type PipeFn func(io.Reader) (io.Reader, error)
func (f PipeFn) Pipe(stdin io.Reader) (io.Reader, error) { return f(stdin) }
func (f PipeFn) Name() string { return reflect.TypeOf(f).Name() }
type readcloser struct {
io.Reader
io.Closer
}