12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- package rcli
- // rcli (Remote Command-Line Interface) is a simple protocol for...
- // serving command-line interfaces remotely.
- //
- // rcli can be used over any transport capable of a) sending binary streams in
- // both directions, and b) capable of half-closing a connection. TCP and Unix sockets
- // are the usual suspects.
- import (
- "errors"
- "flag"
- "fmt"
- "io"
- "log"
- "reflect"
- "strings"
- )
- type Service interface {
- Name() string
- Help() string
- }
- type Cmd func(io.ReadCloser, io.Writer, ...string) error
- type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error
- // FIXME: For reverse compatibility
- func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
- return LocalCall(service, stdin, stdout, args...)
- }
- func LocalCall(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
- if len(args) == 0 {
- args = []string{"help"}
- }
- flags := flag.NewFlagSet("main", flag.ContinueOnError)
- flags.SetOutput(stdout)
- flags.Usage = func() { stdout.Write([]byte(service.Help())) }
- if err := flags.Parse(args); err != nil {
- return err
- }
- cmd := flags.Arg(0)
- log.Printf("%s\n", strings.Join(append(append([]string{service.Name()}, cmd), flags.Args()[1:]...), " "))
- if cmd == "" {
- cmd = "help"
- }
- method := getMethod(service, cmd)
- if method != nil {
- return method(stdin, stdout, flags.Args()[1:]...)
- }
- return errors.New("No such command: " + cmd)
- }
- func getMethod(service Service, name string) Cmd {
- if name == "help" {
- return func(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
- if len(args) == 0 {
- stdout.Write([]byte(service.Help()))
- } else {
- if method := getMethod(service, args[0]); method == nil {
- return errors.New("No such command: " + args[0])
- } else {
- method(stdin, stdout, "--help")
- }
- }
- return nil
- }
- }
- methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
- method, exists := reflect.TypeOf(service).MethodByName(methodName)
- if !exists {
- return nil
- }
- return func(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
- ret := method.Func.CallSlice([]reflect.Value{
- reflect.ValueOf(service),
- reflect.ValueOf(stdin),
- reflect.ValueOf(stdout),
- reflect.ValueOf(args),
- })[0].Interface()
- if ret == nil {
- return nil
- }
- return ret.(error)
- }
- }
- func Subcmd(output io.Writer, name, signature, description string) *flag.FlagSet {
- flags := flag.NewFlagSet(name, flag.ContinueOnError)
- flags.SetOutput(output)
- flags.Usage = func() {
- fmt.Fprintf(output, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
- flags.PrintDefaults()
- }
- return flags
- }
|