141 lines
3.1 KiB
Go
141 lines
3.1 KiB
Go
package telsh
|
|
|
|
|
|
import (
|
|
"io"
|
|
)
|
|
|
|
|
|
// Hander is an abstraction that represents a "running" shell "command".
|
|
//
|
|
// Contrast this with a Producer, which is is an abstraction that
|
|
// represents a shell "command".
|
|
//
|
|
// To use a metaphor, the differences between a Producer and a Handler,
|
|
// is like the difference between a program executable and actually running
|
|
// the program executable.
|
|
//
|
|
// Conceptually, anything that implements the Hander, and then has its Producer
|
|
// registered with ShellHandler.Register() will be available as a command.
|
|
//
|
|
// Note that Handler was intentionally made to be compatible with
|
|
// "os/exec", which is part of the Go standard library.
|
|
type Handler interface {
|
|
Run() error
|
|
|
|
StdinPipe() (io.WriteCloser, error)
|
|
StdoutPipe() (io.ReadCloser, error)
|
|
StderrPipe() (io.ReadCloser, error)
|
|
}
|
|
|
|
|
|
// HandlerFunc is useful to write inline Producers, and provides an alternative to
|
|
// creating something that implements Handler directly.
|
|
//
|
|
// For example:
|
|
//
|
|
// shellHandler := telsh.NewShellHandler()
|
|
//
|
|
// shellHandler.Register("five", telsh.ProducerFunc(
|
|
//
|
|
// func(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
|
//
|
|
// return telsh.PromoteHandlerFunc(
|
|
//
|
|
// func(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
|
// oi.LongWrite(stdout, []byte{'5', '\r', '\n'})
|
|
//
|
|
// return nil
|
|
// },
|
|
// )
|
|
// },
|
|
// ))
|
|
//
|
|
// Note that PromoteHandlerFunc is used to turn a HandlerFunc into a Handler.
|
|
type HandlerFunc func(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string)error
|
|
|
|
|
|
type internalPromotedHandlerFunc struct {
|
|
err error
|
|
fn HandlerFunc
|
|
stdin io.ReadCloser
|
|
stdout io.WriteCloser
|
|
stderr io.WriteCloser
|
|
|
|
stdinPipe io.WriteCloser
|
|
stdoutPipe io.ReadCloser
|
|
stderrPipe io.ReadCloser
|
|
|
|
args []string
|
|
}
|
|
|
|
|
|
// PromoteHandlerFunc turns a HandlerFunc into a Handler.
|
|
func PromoteHandlerFunc(fn HandlerFunc, args ...string) Handler {
|
|
stdin, stdinPipe := io.Pipe()
|
|
stdoutPipe, stdout := io.Pipe()
|
|
stderrPipe, stderr := io.Pipe()
|
|
|
|
argsCopy := make([]string, len(args))
|
|
for i, datum := range args {
|
|
argsCopy[i] = datum
|
|
}
|
|
|
|
handler := internalPromotedHandlerFunc{
|
|
err:nil,
|
|
|
|
fn:fn,
|
|
|
|
stdin:stdin,
|
|
stdout:stdout,
|
|
stderr:stderr,
|
|
|
|
stdinPipe:stdinPipe,
|
|
stdoutPipe:stdoutPipe,
|
|
stderrPipe:stderrPipe,
|
|
|
|
args:argsCopy,
|
|
}
|
|
|
|
return &handler
|
|
}
|
|
|
|
|
|
func (handler *internalPromotedHandlerFunc) Run() error {
|
|
if nil != handler.err {
|
|
return handler.err
|
|
}
|
|
|
|
handler.err = handler.fn(handler.stdin, handler.stdout, handler.stderr, handler.args...)
|
|
|
|
handler.stdin.Close()
|
|
handler.stdout.Close()
|
|
handler.stderr.Close()
|
|
|
|
return handler.err
|
|
}
|
|
|
|
func (handler *internalPromotedHandlerFunc) StdinPipe() (io.WriteCloser, error) {
|
|
if nil != handler.err {
|
|
return nil, handler.err
|
|
}
|
|
|
|
return handler.stdinPipe, nil
|
|
}
|
|
|
|
func (handler *internalPromotedHandlerFunc) StdoutPipe() (io.ReadCloser, error) {
|
|
if nil != handler.err {
|
|
return nil, handler.err
|
|
}
|
|
|
|
return handler.stdoutPipe, nil
|
|
}
|
|
|
|
func (handler *internalPromotedHandlerFunc) StderrPipe() (io.ReadCloser, error) {
|
|
if nil != handler.err {
|
|
return nil, handler.err
|
|
}
|
|
|
|
return handler.stderrPipe, nil
|
|
}
|