From 096b5333b2dafbffccc209863fc1fc49f8b475da Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Sun, 30 Jan 2022 12:17:43 +0100 Subject: [PATCH] Common flags example (#19) --- GUIDE.md | 49 ++++++++++++++++++++++++++++++++++++- example_test.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/GUIDE.md b/GUIDE.md index e0d64d8..96cf200 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -1,7 +1,9 @@ -# acmd guides +# Guide for acmd ## Flag + Example with command like that `./dummy server ./openapi.yml -port=8080` from [dummy](https://github.com/go-dummy/dummy/blob/main/cmd/dummy/main.go) + ```go func run() error { cmds := []acmd.Command{ @@ -36,3 +38,48 @@ func run() error { return r.Run() } ``` + +## Flags propagation + +There is no special methods, config fields to propagate flags to subcommands. However it's not hard to make this, because every command can access predefined flags, which are shared across handlers. + +```go +type commonFlags struct { + IsVerbose bool +} + +// NOTE: should be added before flag.FlagSet method Parse(). +func withCommonFlags(fs *flag.FlagSet) *commonFlags { + c := &commonFlags{} + fs.BoolVar(&c.IsVerbose, "verbose", false, "should app be verbose") + return c +} + +func cmdFoo(ctx context.Context, args []string) error { + fs := flag.NewFlagSet("foo", flag.ContinueOnError) + // NOTE: here add flags for cmdBar as always + + // add common flags, make sure it's before Parse but after all defined flags + common := withCommonFlags(fs) + if err := fs.Parse(args); err != nil { + return err + } + // use commonFlags fields or any other flags that you have defined + return nil +} + +func cmdBar(ctx context.Context, args []string) error { + fs := flag.NewFlagSet("bar", flag.ContinueOnError) + // NOTE: here add flags for cmdFoo as always + + // add common flags, make sure it's before Parse but after all defined flags + common := withCommonFlags(fs) + if err := fs.Parse(args); err != nil { + return err + } + // use commonFlags fields or any other flags that you have defined + return nil +} +``` + +Also see `ExamplePropagateFlags` test. diff --git a/example_test.go b/example_test.go index f83c736..0917277 100644 --- a/example_test.go +++ b/example_test.go @@ -1,6 +1,7 @@ package acmd_test import ( + "bytes" "context" "flag" "fmt" @@ -256,3 +257,67 @@ func ExampleNestedCommands() { // Output: qux } + +func ExamplePropagateFlags() { + testOut := os.Stdout + testArgs := []string{"someapp", "foo", "-dir=test-dir", "--verbose"} + buf := &bytes.Buffer{} + + cmds := []acmd.Command{ + { + Name: "foo", Do: func(ctx context.Context, args []string) error { + fs := flag.NewFlagSet("foo", flag.ContinueOnError) + isRecursive := fs.Bool("r", false, "should file list be recursive") + common := withCommonFlags(fs) + if err := fs.Parse(args); err != nil { + return err + } + if common.IsVerbose { + fmt.Fprintf(buf, "TODO: dir %q, is recursive = %v\n", common.Dir, *isRecursive) + } + return nil + }, + }, + { + Name: "bar", Do: func(ctx context.Context, args []string) error { + fs := flag.NewFlagSet("bar", flag.ContinueOnError) + common := withCommonFlags(fs) + if err := fs.Parse(args); err != nil { + return err + } + if common.IsVerbose { + fmt.Fprintf(buf, "TODO: dir %q\n", common.Dir) + } + return nil + }, + }, + } + + r := acmd.RunnerOf(cmds, acmd.Config{ + AppName: "acmd-example", + AppDescription: "Example of acmd package", + Version: "the best v0.x.y", + Output: testOut, + Args: testArgs, + }) + + if err := r.Run(); err != nil { + panic(err) + } + fmt.Println(buf.String()) + + // Output: TODO: dir "test-dir", is recursive = false +} + +type commonFlags struct { + IsVerbose bool + Dir string +} + +// NOTE: should be added before flag.FlagSet method Parse(). +func withCommonFlags(fs *flag.FlagSet) *commonFlags { + c := &commonFlags{} + fs.BoolVar(&c.IsVerbose, "verbose", false, "should app be verbose") + fs.StringVar(&c.Dir, "dir", ".", "directory to process") + return c +}