From 14e1d2cc61348fb419e56b57ef11ffc244ce7d94 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Thu, 27 Oct 2022 05:55:37 +1100 Subject: [PATCH] Support `Addflags` --- cli.go | 6 +++++- cli_test.go | 33 +++++++++++++++++++++++++++++++++ command.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/cli.go b/cli.go index 4afd5df..ec0959f 100755 --- a/cli.go +++ b/cli.go @@ -47,7 +47,6 @@ func (c *Cli) SetErrorFunction(fn func(string, error) error) { // Abort - Prints the given error and terminates the application. func (c *Cli) Abort(err error) { log.Fatal(err) - os.Exit(1) } // AddCommand - Adds a command to the application. @@ -120,6 +119,11 @@ func (c *Cli) IntFlag(name, description string, variable *int) *Cli { return c } +func (c *Cli) AddFlags(flags interface{}) *Cli { + c.rootCommand.AddFlags(flags) + return c +} + // Action - Define an action from this command. func (c *Cli) Action(callback Action) *Cli { c.rootCommand.Action(callback) diff --git a/cli_test.go b/cli_test.go index 63f77ea..38b1962 100644 --- a/cli_test.go +++ b/cli_test.go @@ -72,3 +72,36 @@ func TestCli(t *testing.T) { c.LongDescription("long description") }) } + +type testStruct struct { + Mode string `name:"mode" description:"The mode of build"` +} + +func TestCli_AddFlags(t *testing.T) { + c := NewCli("test", "description", "0") + + ts := &testStruct{} + c.AddFlags(ts) + + modeFlag := c.rootCommand.flags.Lookup("mode") + if modeFlag == nil { + t.Errorf("expected flag mode to be added") + } + if modeFlag.Name != "mode" { + t.Errorf("expected flag name to be added") + } + if modeFlag.Usage != "The mode of build" { + t.Errorf("expected flag description to be added") + } + + c.Action(func() error { + if ts.Mode != "123" { + t.Errorf("expected flag value to be set") + } + return nil + }) + e := c.Run("-mode", "123") + if e != nil { + t.Errorf("expected no error, got %v", e) + } +} diff --git a/command.go b/command.go index 987aa39..ff81e01 100644 --- a/command.go +++ b/command.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "os" + "reflect" "strings" ) @@ -221,6 +222,42 @@ func (c *Command) BoolFlag(name, description string, variable *bool) *Command { return c } +func (c *Command) AddFlags(optionStruct interface{}) *Command { + // use reflection to determine if this is a pointer to a struct + // if not, panic + + t := reflect.TypeOf(optionStruct) + if t.Kind() != reflect.Ptr { + panic("AddFlags requires a pointer to a struct") + } + + // Iterate through the fields of the struct reading the struct tags + // and adding the flags + v := reflect.ValueOf(optionStruct).Elem() + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + tag := t.Elem().Field(i).Tag + name := tag.Get("name") + description := tag.Get("description") + if name == "" { + name = strings.ToLower(t.Elem().Field(i).Name) + } + if description == "" { + description = "No description" + } + switch field.Kind() { + case reflect.Bool: + c.BoolFlag(name, description, field.Addr().Interface().(*bool)) + case reflect.String: + c.StringFlag(name, description, field.Addr().Interface().(*string)) + case reflect.Int: + c.IntFlag(name, description, field.Addr().Interface().(*int)) + } + } + + return c +} + // StringFlag - Adds a string flag to the command func (c *Command) StringFlag(name, description string, variable *string) *Command { c.flags.StringVar(variable, name, *variable, description)