From ae8d932413452718007ff70a4e50ccf9729afd87 Mon Sep 17 00:00:00 2001 From: dearchap Date: Fri, 28 Oct 2022 09:17:13 -0400 Subject: [PATCH] Feature:(issue_269) Allow external package flag definitions (#1540) * Feature:(issue_269) Add compatibility with external package flag definitions * Add tests * Add defer to remove global flag * Use ptr to receiver for extFlag * Add const for flag prefix to ignore --- app.go | 10 ++++++++++ app_test.go | 36 ++++++++++++++++++++++++++++++++++++ flag_ext.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 flag_ext.go diff --git a/app.go b/app.go index 9bea11e..3fb5c16 100644 --- a/app.go +++ b/app.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "sort" + "strings" "time" ) @@ -20,6 +21,7 @@ var ( errInvalidActionType = NewExitError("ERROR invalid Action type. "+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("See %s", appActionDeprecationURL), 2) + ignoreFlagPrefix = "test." // this is to ignore test flags when adding flags from other packages SuggestFlag SuggestFlagFunc = suggestFlag SuggestCommand SuggestCommandFunc = suggestCommand @@ -197,6 +199,14 @@ func (a *App) Setup() { a.ErrWriter = os.Stderr } + // add global flags added by other packages + flag.VisitAll(func(f *flag.Flag) { + // skip test flags + if !strings.HasPrefix(f.Name, ignoreFlagPrefix) { + a.Flags = append(a.Flags, &extFlag{f}) + } + }) + var newCommands []*Command for _, c := range a.Commands { diff --git a/app_test.go b/app_test.go index 9b1a347..d24fb63 100644 --- a/app_test.go +++ b/app_test.go @@ -643,6 +643,42 @@ func TestApp_RunDefaultCommandWithFlags(t *testing.T) { } } +func TestApp_FlagsFromExtPackage(t *testing.T) { + + var someint int + flag.IntVar(&someint, "epflag", 2, "ext package flag usage") + + // Based on source code we can reset the global flag parsing this way + defer func() { + flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) + }() + + a := &App{ + Flags: []Flag{ + &StringFlag{ + Name: "carly", + Aliases: []string{"c"}, + Required: false, + }, + &BoolFlag{ + Name: "jimbob", + Aliases: []string{"j"}, + Required: false, + Value: true, + }, + }, + } + + err := a.Run([]string{"foo", "-c", "cly", "--epflag", "10"}) + if err != nil { + t.Error(err) + } + + if someint != 10 { + t.Errorf("Expected 10 got %d for someint", someint) + } +} + func TestApp_Setup_defaultsReader(t *testing.T) { app := &App{} app.Setup() diff --git a/flag_ext.go b/flag_ext.go new file mode 100644 index 0000000..64da59e --- /dev/null +++ b/flag_ext.go @@ -0,0 +1,48 @@ +package cli + +import "flag" + +type extFlag struct { + f *flag.Flag +} + +func (e *extFlag) Apply(fs *flag.FlagSet) error { + fs.Var(e.f.Value, e.f.Name, e.f.Usage) + return nil +} + +func (e *extFlag) Names() []string { + return []string{e.f.Name} +} + +func (e *extFlag) IsSet() bool { + return false +} + +func (e *extFlag) String() string { + return FlagStringer(e) +} + +func (e *extFlag) IsVisible() bool { + return true +} + +func (e *extFlag) TakesValue() bool { + return false +} + +func (e *extFlag) GetUsage() string { + return e.f.Usage +} + +func (e *extFlag) GetValue() string { + return e.f.Value.String() +} + +func (e *extFlag) GetDefaultText() string { + return e.f.DefValue +} + +func (e *extFlag) GetEnvVars() []string { + return nil +}