From 919a37acb53c7e9bc45e4ffeaa7dec70e0016e7c Mon Sep 17 00:00:00 2001 From: Marc-Antoine Argenton Date: Fri, 5 May 2023 21:37:16 -0700 Subject: [PATCH] - Update README - Fix issues / crashes with sub-commands handling --- .vscode/launch.json | 2 +- README.md | 50 ++++++++++++++++++++++++++++++++++++++ cli.go | 2 +- pkg/cli/cmd.go | 3 +++ pkg/option/optionset.go | 3 +++ sample/subcmd-demo/main.go | 2 +- 6 files changed, 59 insertions(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 236f3c3..15d0edc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -37,7 +37,7 @@ "mode": "auto", "program": "${workspaceFolder}/sample/subcmd-demo", "args": [ - "migrate", + // "migrate", ] } ] diff --git a/README.md b/README.md index 9d0b97a..810ca09 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,56 @@ completion machinery can be disabled by setting `cmd.DisableCompletion` on the root command. +## Sub-command support + +The `go-cli` package also provides support for sub-commands definition, to build +a command-line interface similar to `git`, `go` and `kubectl`, where a single +binary exposes a number of related actions. + +Sub-commands are defined by adding multiple named commands to the `SubCommands` +field of the main command definition: + +``` +func main() { + cli.Run(&cli.Command{ + SubCommands: []cli.Command{ + { + Name: "migrate", + Description: "run DB migration", + Handler: &MigrateCmd{}, + }, + .... + }, + }) +} +``` + +### No prefix argument + +When defining sub-commands with `go-cli`, the sequence of sub-commands verbs and +noun are expected to appear at the beginning of the command-line, before any +option or argument. This is in contrast to e.g. `git` which allows a subset of +global arguments to appear before the sub-command. + +With `go-cli`, each sub-command has its own set of command-line options and +arguments, defined in the command struct. Options that are shared between some +or all sub-commands can be defined on a shared nested struct. + + +### Bare command with sub-commands + +In general, command-line utilities either have no sub-commands or only +sub-commands. With `gp-cli`, you can have both a default behavior and a list of +sub-command to provide additional related functionalities. + + +### Help + +In general, utilities with sub-commands provide help through an `help` +sub-command that precede the specific command the inquiry is about. With +`go-cli`, help is by default requested with the `--help` option. + + ## Enum support To help with command-line handling of enumerated types, the `go-cli` package diff --git a/cli.go b/cli.go index fc7f543..974e4e0 100644 --- a/cli.go +++ b/cli.go @@ -49,7 +49,7 @@ func Run(cmd *Command) { } else if errors.Is(err, cli.ErrHelpRequested) { fmt.Print(cmd.Usage()) - + os.Exit(1) } else if errors.Is(err, cli.ErrVersionRequested) { var version = cmd.Version() if version != "" { diff --git a/pkg/cli/cmd.go b/pkg/cli/cmd.go index 27f19a4..32a94aa 100644 --- a/pkg/cli/cmd.go +++ b/pkg/cli/cmd.go @@ -123,6 +123,9 @@ func (cmd *Command) Run() error { return err } + if cmd.Handler == nil { + return ErrHelpRequested + } if err := cmd.Handler.Run(); err != nil { return err } diff --git a/pkg/option/optionset.go b/pkg/option/optionset.go index c8bf103..c58160f 100644 --- a/pkg/option/optionset.go +++ b/pkg/option/optionset.go @@ -23,6 +23,9 @@ type Set struct { // that can be set through commandline arguments func NewOptionSet(v interface{}) (*Set, error) { var pv = reflect.ValueOf(v) + if !pv.IsValid() { + return &Set{}, nil + } if pv.Kind() != reflect.Ptr || pv.IsNil() || pv.Elem().Kind() != reflect.Struct { return nil, fmt.Errorf("invalid argument of type '%v', non-null pointer to struct expected", pv.Type()) } diff --git a/sample/subcmd-demo/main.go b/sample/subcmd-demo/main.go index cf16cb5..a58b291 100644 --- a/sample/subcmd-demo/main.go +++ b/sample/subcmd-demo/main.go @@ -9,7 +9,7 @@ import ( func main() { cli.Run(&cli.Command{ - Handler: &ServerCmd{}, + // Handler: &ServerCmd{}, Description: "API server for demo service", SubCommands: []cli.Command{