diff --git a/app.go b/app.go index 2ffacd512c..9f72f1b4f3 100644 --- a/app.go +++ b/app.go @@ -342,6 +342,10 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { } } + if err = runFlagActions(cCtx, a.Flags); err != nil { + return err + } + var c *Command args := cCtx.Args() if args.Present() { @@ -523,6 +527,10 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } } + if err = runFlagActions(cCtx, a.Flags); err != nil { + return err + } + args := cCtx.Args() if args.Present() { name := args.First() @@ -646,6 +654,26 @@ func (a *App) argsWithDefaultCommand(oldArgs Args) Args { return oldArgs } +func runFlagActions(c *Context, fs []Flag) error { + for _, f := range fs { + isSet := false + for _, name := range f.Names() { + if c.IsSet(name) { + isSet = true + break + } + } + if isSet { + if af, ok := f.(ActionableFlag); ok { + if err := af.RunAction(c); err != nil { + return err + } + } + } + } + return nil +} + // Author represents someone who has contributed to a cli project. type Author struct { Name string // The Authors name diff --git a/app_test.go b/app_test.go index 64316fc79e..f3cbb1edaf 100644 --- a/app_test.go +++ b/app_test.go @@ -9,8 +9,10 @@ import ( "io/ioutil" "os" "reflect" + "strconv" "strings" "testing" + "time" ) var ( @@ -2357,6 +2359,10 @@ func (c *customBoolFlag) Apply(set *flag.FlagSet) error { return nil } +func (c *customBoolFlag) RunAction(*Context) error { + return nil +} + func (c *customBoolFlag) IsSet() bool { return false } @@ -2576,3 +2582,392 @@ func TestSetupInitializesOnlyNilWriters(t *testing.T) { t.Errorf("expected a.Writer to be os.Stdout") } } + +type stringGeneric struct { + value string +} + +func (s *stringGeneric) Set(value string) error { + s.value = value + return nil +} + +func (s *stringGeneric) String() string { + return s.value +} + +func TestFlagAction(t *testing.T) { + stringFlag := &StringFlag{ + Name: "f_string", + Action: func(c *Context, v string) error { + if v == "" { + return fmt.Errorf("empty string") + } + c.App.Writer.Write([]byte(v + " ")) + return nil + }, + } + app := &App{ + Name: "app", + Commands: []*Command{ + { + Name: "c1", + Flags: []Flag{stringFlag}, + Action: func(ctx *Context) error { return nil }, + Subcommands: []*Command{ + { + Name: "sub1", + Action: func(ctx *Context) error { return nil }, + Flags: []Flag{stringFlag}, + }, + }, + }, + }, + Flags: []Flag{ + stringFlag, + &StringFlag{ + Name: "f_no_action", + }, + &StringSliceFlag{ + Name: "f_string_slice", + Action: func(c *Context, v []string) error { + if v[0] == "err" { + return fmt.Errorf("error string slice") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &BoolFlag{ + Name: "f_bool", + Action: func(c *Context, v bool) error { + if !v { + return fmt.Errorf("value is false") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%t ", v))) + return nil + }, + }, + &DurationFlag{ + Name: "f_duration", + Action: func(c *Context, v time.Duration) error { + if v == 0 { + return fmt.Errorf("empty duration") + } + c.App.Writer.Write([]byte(v.String() + " ")) + return nil + }, + }, + &Float64Flag{ + Name: "f_float64", + Action: func(c *Context, v float64) error { + if v < 0 { + return fmt.Errorf("negative float64") + } + c.App.Writer.Write([]byte(strconv.FormatFloat(v, 'f', -1, 64) + " ")) + return nil + }, + }, + &Float64SliceFlag{ + Name: "f_float64_slice", + Action: func(c *Context, v []float64) error { + if len(v) > 0 && v[0] < 0 { + return fmt.Errorf("invalid float64 slice") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &GenericFlag{ + Name: "f_generic", + Value: new(stringGeneric), + Action: func(c *Context, v interface{}) error { + fmt.Printf("%T %v\n", v, v) + switch vv := v.(type) { + case *stringGeneric: + if vv.value == "" { + return fmt.Errorf("generic value not set") + } + } + + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &IntFlag{ + Name: "f_int", + Action: func(c *Context, v int) error { + if v < 0 { + return fmt.Errorf("negative int") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &IntSliceFlag{ + Name: "f_int_slice", + Action: func(c *Context, v []int) error { + if len(v) > 0 && v[0] < 0 { + return fmt.Errorf("invalid int slice") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &Int64Flag{ + Name: "f_int64", + Action: func(c *Context, v int64) error { + if v < 0 { + return fmt.Errorf("negative int64") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &Int64SliceFlag{ + Name: "f_int64_slice", + Action: func(c *Context, v []int64) error { + if len(v) > 0 && v[0] < 0 { + return fmt.Errorf("invalid int64 slice") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &PathFlag{ + Name: "f_path", + Action: func(c *Context, v string) error { + if v == "" { + return fmt.Errorf("empty path") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &TimestampFlag{ + Name: "f_timestamp", + Layout: "2006-01-02 15:04:05", + Action: func(c *Context, v *time.Time) error { + if v.IsZero() { + return fmt.Errorf("zero timestamp") + } + c.App.Writer.Write([]byte(v.Format(time.RFC3339) + " ")) + return nil + }, + }, + &UintFlag{ + Name: "f_uint", + Action: func(c *Context, v uint) error { + if v == 0 { + return fmt.Errorf("zero uint") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + &Uint64Flag{ + Name: "f_uint64", + Action: func(c *Context, v uint64) error { + if v == 0 { + return fmt.Errorf("zero uint64") + } + c.App.Writer.Write([]byte(fmt.Sprintf("%v ", v))) + return nil + }, + }, + }, + Action: func(ctx *Context) error { return nil }, + } + + tests := []struct { + name string + args []string + err error + exp string + }{ + { + name: "flag_string", + args: []string{"app", "--f_string=string"}, + exp: "string ", + }, + { + name: "flag_string_error", + args: []string{"app", "--f_string="}, + err: fmt.Errorf("empty string"), + }, + { + name: "flag_string_slice", + args: []string{"app", "--f_string_slice=s1,s2,s3"}, + exp: "[s1 s2 s3] ", + }, + { + name: "flag_string_slice_error", + args: []string{"app", "--f_string_slice=err"}, + err: fmt.Errorf("error string slice"), + }, + { + name: "flag_bool", + args: []string{"app", "--f_bool"}, + exp: "true ", + }, + { + name: "flag_bool_error", + args: []string{"app", "--f_bool=false"}, + err: fmt.Errorf("value is false"), + }, + { + name: "flag_duration", + args: []string{"app", "--f_duration=1h30m20s"}, + exp: "1h30m20s ", + }, + { + name: "flag_duration_error", + args: []string{"app", "--f_duration=0"}, + err: fmt.Errorf("empty duration"), + }, + { + name: "flag_float64", + args: []string{"app", "--f_float64=3.14159"}, + exp: "3.14159 ", + }, + { + name: "flag_float64_error", + args: []string{"app", "--f_float64=-1"}, + err: fmt.Errorf("negative float64"), + }, + { + name: "flag_float64_slice", + args: []string{"app", "--f_float64_slice=1.1,2.2,3.3"}, + exp: "[1.1 2.2 3.3] ", + }, + { + name: "flag_float64_slice_error", + args: []string{"app", "--f_float64_slice=-1"}, + err: fmt.Errorf("invalid float64 slice"), + }, + { + name: "flag_generic", + args: []string{"app", "--f_generic=1"}, + exp: "1 ", + }, + { + name: "flag_generic_error", + args: []string{"app", "--f_generic="}, + err: fmt.Errorf("generic value not set"), + }, + { + name: "flag_int", + args: []string{"app", "--f_int=1"}, + exp: "1 ", + }, + { + name: "flag_int_error", + args: []string{"app", "--f_int=-1"}, + err: fmt.Errorf("negative int"), + }, + { + name: "flag_int_slice", + args: []string{"app", "--f_int_slice=1,2,3"}, + exp: "[1 2 3] ", + }, + { + name: "flag_int_slice_error", + args: []string{"app", "--f_int_slice=-1"}, + err: fmt.Errorf("invalid int slice"), + }, + { + name: "flag_int64", + args: []string{"app", "--f_int64=1"}, + exp: "1 ", + }, + { + name: "flag_int64_error", + args: []string{"app", "--f_int64=-1"}, + err: fmt.Errorf("negative int64"), + }, + { + name: "flag_int64_slice", + args: []string{"app", "--f_int64_slice=1,2,3"}, + exp: "[1 2 3] ", + }, + { + name: "flag_int64_slice", + args: []string{"app", "--f_int64_slice=-1"}, + err: fmt.Errorf("invalid int64 slice"), + }, + { + name: "flag_path", + args: []string{"app", "--f_path=/root"}, + exp: "/root ", + }, + { + name: "flag_path_error", + args: []string{"app", "--f_path="}, + err: fmt.Errorf("empty path"), + }, + { + name: "flag_timestamp", + args: []string{"app", "--f_timestamp", "2022-05-01 02:26:20"}, + exp: "2022-05-01T02:26:20Z ", + }, + { + name: "flag_timestamp_error", + args: []string{"app", "--f_timestamp", "0001-01-01 00:00:00"}, + err: fmt.Errorf("zero timestamp"), + }, + { + name: "flag_uint", + args: []string{"app", "--f_uint=1"}, + exp: "1 ", + }, + { + name: "flag_uint_error", + args: []string{"app", "--f_uint=0"}, + err: fmt.Errorf("zero uint"), + }, + { + name: "flag_uint64", + args: []string{"app", "--f_uint64=1"}, + exp: "1 ", + }, + { + name: "flag_uint64_error", + args: []string{"app", "--f_uint64=0"}, + err: fmt.Errorf("zero uint64"), + }, + { + name: "flag_no_action", + args: []string{"app", "--f_no_action="}, + exp: "", + }, + { + name: "command_flag", + args: []string{"app", "c1", "--f_string=c1"}, + exp: "c1 ", + }, + { + name: "subCommand_flag", + args: []string{"app", "c1", "sub1", "--f_string=sub1"}, + exp: "sub1 ", + }, + { + name: "mixture", + args: []string{"app", "--f_string=app", "--f_uint=1", "--f_int_slice=1,2,3", "--f_duration=1h30m20s", "c1", "--f_string=c1", "sub1", "--f_string=sub1"}, + exp: "app 1h30m20s [1 2 3] 1 c1 sub1 ", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + buf := new(bytes.Buffer) + app.Writer = buf + err := app.Run(test.args) + if test.err != nil { + expect(t, err, test.err) + } else { + expect(t, err, nil) + expect(t, buf.String(), test.exp) + } + }) + } +} diff --git a/cmd/urfave-cli-genflags/go.sum b/cmd/urfave-cli-genflags/go.sum index 98211271c8..e59916dc6f 100644 --- a/cmd/urfave-cli-genflags/go.sum +++ b/cmd/urfave-cli-genflags/go.sum @@ -1,5 +1,3 @@ -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -10,7 +8,6 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/command.go b/command.go index 13b79de46d..cd8ea91c70 100644 --- a/command.go +++ b/command.go @@ -165,6 +165,10 @@ func (c *Command) Run(ctx *Context) (err error) { } } + if err = runFlagActions(cCtx, c.Flags); err != nil { + return err + } + if c.Action == nil { c.Action = helpSubcommand.Action } diff --git a/flag-spec.yaml b/flag-spec.yaml index 5c480bb09f..76480ba47c 100644 --- a/flag-spec.yaml +++ b/flag-spec.yaml @@ -7,47 +7,77 @@ flag_types: - name: Count type: int pointer: true + - name: Action + type: "func(*Context, bool) error" float64: + struct_fields: + - name: Action + type: "func(*Context, float64) error" Float64Slice: value_pointer: true skip_interfaces: - fmt.Stringer + struct_fields: + - name: Action + type: "func(*Context, []float64) error" int: struct_fields: - name: Base type: int + - name: Action + type: "func(*Context, int) error" IntSlice: value_pointer: true skip_interfaces: - fmt.Stringer + struct_fields: + - name: Action + type: "func(*Context, []int) error" int64: struct_fields: - name: Base type: int + - name: Action + type: "func(*Context, int64) error" Int64Slice: value_pointer: true skip_interfaces: - fmt.Stringer + struct_fields: + - name: Action + type: "func(*Context, []int64) error" uint: struct_fields: - name: Base type: int + - name: Action + type: "func(*Context, uint) error" UintSlice: value_pointer: true skip_interfaces: - fmt.Stringer + struct_fields: + - name: Action + type: "func(*Context, []uint) error" uint64: struct_fields: - name: Base type: int + - name: Action + type: "func(*Context, uint64) error" Uint64Slice: value_pointer: true skip_interfaces: - fmt.Stringer + struct_fields: + - name: Action + type: "func(*Context, []uint64) error" string: struct_fields: - name: TakesFile type: bool + - name: Action + type: "func(*Context, string) error" StringSlice: value_pointer: true skip_interfaces: @@ -55,7 +85,12 @@ flag_types: struct_fields: - name: TakesFile type: bool + - name: Action + type: "func(*Context, []string) error" time.Duration: + struct_fields: + - name: Action + type: "func(*Context, time.Duration) error" Timestamp: value_pointer: true struct_fields: @@ -63,12 +98,18 @@ flag_types: type: string - name: Timezone type: "*time.Location" + - name: Action + type: "func(*Context, *time.Time) error" Generic: no_destination_pointer: true struct_fields: - name: TakesFile type: bool + - name: Action + type: "func(*Context, interface{}) error" Path: struct_fields: - name: TakesFile type: bool + - name: Action + type: "func(*Context, Path) error" diff --git a/flag.go b/flag.go index 050bb4b1d1..7b5ec498c7 100644 --- a/flag.go +++ b/flag.go @@ -83,6 +83,12 @@ func (f FlagsByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +// ActionableFlag is an interface that wraps Flag interface and RunAction operation. +type ActionableFlag interface { + Flag + RunAction(*Context) error +} + // Flag is a common interface related to parsing flags in cli. // For more advanced flag parsing techniques, it is recommended that // this interface be implemented. diff --git a/flag_bool.go b/flag_bool.go index cb937ae65d..0be27e3aa6 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -92,6 +92,15 @@ func (f *BoolFlag) GetEnvVars() []string { return f.EnvVars } +// RunAction executes flag action if set +func (f *BoolFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Bool(f.Name)) + } + + return nil +} + // Apply populates the flag given the flag set and environment func (f *BoolFlag) Apply(set *flag.FlagSet) error { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { diff --git a/flag_duration.go b/flag_duration.go index 5178c6ae12..31db4102e6 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -70,6 +70,15 @@ func (f *DurationFlag) Get(ctx *Context) time.Duration { return ctx.Duration(f.Name) } +// RunAction executes flag action if set +func (f *DurationFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Duration(f.Name)) + } + + return nil +} + // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (cCtx *Context) Duration(name string) time.Duration { diff --git a/flag_float64.go b/flag_float64.go index 2d31739bc6..bce26c1958 100644 --- a/flag_float64.go +++ b/flag_float64.go @@ -70,6 +70,15 @@ func (f *Float64Flag) Get(ctx *Context) float64 { return ctx.Float64(f.Name) } +// RunAction executes flag action if set +func (f *Float64Flag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Float64(f.Name)) + } + + return nil +} + // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (cCtx *Context) Float64(name string) float64 { diff --git a/flag_float64_slice.go b/flag_float64_slice.go index e4aff73da0..2cb5e4adfa 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -181,6 +181,15 @@ func (f *Float64SliceFlag) stringify() string { return stringifySliceFlag(f.Usage, f.Names(), defaultVals) } +// RunAction executes flag action if set +func (f *Float64SliceFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Float64Slice(f.Name)) + } + + return nil +} + // Float64Slice looks up the value of a local Float64SliceFlag, returns // nil if not found func (cCtx *Context) Float64Slice(name string) []float64 { diff --git a/flag_generic.go b/flag_generic.go index 6a19aef36c..5034728c42 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -73,6 +73,15 @@ func (f *GenericFlag) Get(ctx *Context) interface{} { return ctx.Generic(f.Name) } +// RunAction executes flag action if set +func (f *GenericFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Generic(f.Name)) + } + + return nil +} + // Generic looks up the value of a local GenericFlag, returns // nil if not found func (cCtx *Context) Generic(name string) interface{} { diff --git a/flag_int.go b/flag_int.go index 0f5c403b3d..af98e936fb 100644 --- a/flag_int.go +++ b/flag_int.go @@ -71,6 +71,15 @@ func (f *IntFlag) Get(ctx *Context) int { return ctx.Int(f.Name) } +// RunAction executes flag action if set +func (f *IntFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Int(f.Name)) + } + + return nil +} + // Int looks up the value of a local IntFlag, returns // 0 if not found func (cCtx *Context) Int(name string) int { diff --git a/flag_int64.go b/flag_int64.go index a392275def..ebe46d21fc 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -70,6 +70,15 @@ func (f *Int64Flag) Get(ctx *Context) int64 { return ctx.Int64(f.Name) } +// RunAction executes flag action if set +func (f *Int64Flag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Int64(f.Name)) + } + + return nil +} + // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (cCtx *Context) Int64(name string) int64 { diff --git a/flag_int64_slice.go b/flag_int64_slice.go index ead4e77570..d4a11b6a81 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -179,6 +179,15 @@ func (f *Int64SliceFlag) stringify() string { return stringifySliceFlag(f.Usage, f.Names(), defaultVals) } +// RunAction executes flag action if set +func (f *Int64SliceFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Int64Slice(f.Name)) + } + + return nil +} + // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (cCtx *Context) Int64Slice(name string) []int64 { diff --git a/flag_int_slice.go b/flag_int_slice.go index b40e0d8d1a..2cabe7202f 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -179,6 +179,15 @@ func (f *IntSliceFlag) Get(ctx *Context) []int { return ctx.IntSlice(f.Name) } +// RunAction executes flag action if set +func (f *IntSliceFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.IntSlice(f.Name)) + } + + return nil +} + func (f *IntSliceFlag) stringify() string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { diff --git a/flag_path.go b/flag_path.go index 7c87a8900d..911819db94 100644 --- a/flag_path.go +++ b/flag_path.go @@ -67,6 +67,15 @@ func (f *PathFlag) Get(ctx *Context) string { return ctx.Path(f.Name) } +// RunAction executes flag action if set +func (f *PathFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Path(f.Name)) + } + + return nil +} + // Path looks up the value of a local PathFlag, returns // "" if not found func (cCtx *Context) Path(name string) string { diff --git a/flag_string.go b/flag_string.go index c8da38f92d..b7163ba6f6 100644 --- a/flag_string.go +++ b/flag_string.go @@ -65,6 +65,15 @@ func (f *StringFlag) Get(ctx *Context) string { return ctx.String(f.Name) } +// RunAction executes flag action if set +func (f *StringFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.String(f.Name)) + } + + return nil +} + // String looks up the value of a local StringFlag, returns // "" if not found func (cCtx *Context) String(name string) string { diff --git a/flag_string_slice.go b/flag_string_slice.go index 9d69342db1..7b46a24742 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -171,6 +171,15 @@ func (f *StringSliceFlag) stringify() string { return stringifySliceFlag(f.Usage, f.Names(), defaultVals) } +// RunAction executes flag action if set +func (f *StringSliceFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.StringSlice(f.Name)) + } + + return nil +} + // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (cCtx *Context) StringSlice(name string) []string { diff --git a/flag_timestamp.go b/flag_timestamp.go index 16f42dd011..17bc8d7571 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -148,6 +148,15 @@ func (f *TimestampFlag) Get(ctx *Context) *time.Time { return ctx.Timestamp(f.Name) } +// RunAction executes flag action if set +func (f *TimestampFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Timestamp(f.Name)) + } + + return nil +} + // Timestamp gets the timestamp from a flag name func (cCtx *Context) Timestamp(name string) *time.Time { if fs := cCtx.lookupFlagSet(name); fs != nil { diff --git a/flag_uint.go b/flag_uint.go index d25ff73ad7..f9acb6d06c 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -46,6 +46,15 @@ func (f *UintFlag) Apply(set *flag.FlagSet) error { return nil } +// RunAction executes flag action if set +func (f *UintFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Uint(f.Name)) + } + + return nil +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *UintFlag) GetValue() string { diff --git a/flag_uint64.go b/flag_uint64.go index 975c73393b..09590d7353 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -46,6 +46,15 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) error { return nil } +// RunAction executes flag action if set +func (f *Uint64Flag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Uint64(f.Name)) + } + + return nil +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Uint64Flag) GetValue() string { diff --git a/godoc-current.txt b/godoc-current.txt index fbeeb5fb8b..d41dc97a19 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -250,6 +250,13 @@ TYPES type ActionFunc func(*Context) error ActionFunc is the action to execute when no subcommands are specified +type ActionableFlag interface { + Flag + RunAction(*Context) error +} + ActionableFlag is an interface that wraps Flag interface and RunAction + operation. + type AfterFunc func(*Context) error AfterFunc is an action to execute after any subcommands are run, but after the subcommand has finished it is run even if Action() panics @@ -491,6 +498,9 @@ func (f *BoolFlag) IsVisible() bool func (f *BoolFlag) Names() []string Names returns the names of the flag +func (f *BoolFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *BoolFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -798,6 +808,9 @@ func (f *DurationFlag) IsVisible() bool func (f *DurationFlag) Names() []string Names returns the names of the flag +func (f *DurationFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *DurationFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -971,6 +984,9 @@ func (f *Float64Flag) IsVisible() bool func (f *Float64Flag) Names() []string Names returns the names of the flag +func (f *Float64Flag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Float64Flag) String() string String returns a readable representation of this value (for usage defaults) @@ -1056,6 +1072,9 @@ func (f *Float64SliceFlag) IsVisible() bool func (f *Float64SliceFlag) Names() []string Names returns the names of the flag +func (f *Float64SliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Float64SliceFlag) SetDestination(slice []float64) func (f *Float64SliceFlag) SetValue(slice []float64) @@ -1129,6 +1148,9 @@ func (f *GenericFlag) IsVisible() bool func (f *GenericFlag) Names() []string Names returns the names of the flag +func (f *GenericFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *GenericFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1191,6 +1213,9 @@ func (f *Int64Flag) IsVisible() bool func (f *Int64Flag) Names() []string Names returns the names of the flag +func (f *Int64Flag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Int64Flag) String() string String returns a readable representation of this value (for usage defaults) @@ -1276,6 +1301,9 @@ func (f *Int64SliceFlag) IsVisible() bool func (f *Int64SliceFlag) Names() []string Names returns the names of the flag +func (f *Int64SliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Int64SliceFlag) SetDestination(slice []int64) func (f *Int64SliceFlag) SetValue(slice []int64) @@ -1342,6 +1370,9 @@ func (f *IntFlag) IsVisible() bool func (f *IntFlag) Names() []string Names returns the names of the flag +func (f *IntFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *IntFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1431,6 +1462,9 @@ func (f *IntSliceFlag) IsVisible() bool func (f *IntSliceFlag) Names() []string Names returns the names of the flag +func (f *IntSliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *IntSliceFlag) SetDestination(slice []int) func (f *IntSliceFlag) SetValue(slice []int) @@ -1531,6 +1565,9 @@ func (f *PathFlag) IsVisible() bool func (f *PathFlag) Names() []string Names returns the names of the flag +func (f *PathFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *PathFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1668,6 +1705,9 @@ func (f *StringFlag) IsVisible() bool func (f *StringFlag) Names() []string Names returns the names of the flag +func (f *StringFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *StringFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1755,6 +1795,9 @@ func (f *StringSliceFlag) IsVisible() bool func (f *StringSliceFlag) Names() []string Names returns the names of the flag +func (f *StringSliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *StringSliceFlag) SetDestination(slice []string) func (f *StringSliceFlag) SetValue(slice []string) @@ -1856,6 +1899,9 @@ func (f *TimestampFlag) IsVisible() bool func (f *TimestampFlag) Names() []string Names returns the names of the flag +func (f *TimestampFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *TimestampFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1918,6 +1964,9 @@ func (f *Uint64Flag) IsVisible() bool func (f *Uint64Flag) Names() []string Names returns the names of the flag +func (f *Uint64Flag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Uint64Flag) String() string String returns a readable representation of this value (for usage defaults) @@ -2063,6 +2112,9 @@ func (f *UintFlag) IsVisible() bool func (f *UintFlag) Names() []string Names returns the names of the flag +func (f *UintFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *UintFlag) String() string String returns a readable representation of this value (for usage defaults) diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index fbeeb5fb8b..d41dc97a19 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -250,6 +250,13 @@ TYPES type ActionFunc func(*Context) error ActionFunc is the action to execute when no subcommands are specified +type ActionableFlag interface { + Flag + RunAction(*Context) error +} + ActionableFlag is an interface that wraps Flag interface and RunAction + operation. + type AfterFunc func(*Context) error AfterFunc is an action to execute after any subcommands are run, but after the subcommand has finished it is run even if Action() panics @@ -491,6 +498,9 @@ func (f *BoolFlag) IsVisible() bool func (f *BoolFlag) Names() []string Names returns the names of the flag +func (f *BoolFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *BoolFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -798,6 +808,9 @@ func (f *DurationFlag) IsVisible() bool func (f *DurationFlag) Names() []string Names returns the names of the flag +func (f *DurationFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *DurationFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -971,6 +984,9 @@ func (f *Float64Flag) IsVisible() bool func (f *Float64Flag) Names() []string Names returns the names of the flag +func (f *Float64Flag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Float64Flag) String() string String returns a readable representation of this value (for usage defaults) @@ -1056,6 +1072,9 @@ func (f *Float64SliceFlag) IsVisible() bool func (f *Float64SliceFlag) Names() []string Names returns the names of the flag +func (f *Float64SliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Float64SliceFlag) SetDestination(slice []float64) func (f *Float64SliceFlag) SetValue(slice []float64) @@ -1129,6 +1148,9 @@ func (f *GenericFlag) IsVisible() bool func (f *GenericFlag) Names() []string Names returns the names of the flag +func (f *GenericFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *GenericFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1191,6 +1213,9 @@ func (f *Int64Flag) IsVisible() bool func (f *Int64Flag) Names() []string Names returns the names of the flag +func (f *Int64Flag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Int64Flag) String() string String returns a readable representation of this value (for usage defaults) @@ -1276,6 +1301,9 @@ func (f *Int64SliceFlag) IsVisible() bool func (f *Int64SliceFlag) Names() []string Names returns the names of the flag +func (f *Int64SliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Int64SliceFlag) SetDestination(slice []int64) func (f *Int64SliceFlag) SetValue(slice []int64) @@ -1342,6 +1370,9 @@ func (f *IntFlag) IsVisible() bool func (f *IntFlag) Names() []string Names returns the names of the flag +func (f *IntFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *IntFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1431,6 +1462,9 @@ func (f *IntSliceFlag) IsVisible() bool func (f *IntSliceFlag) Names() []string Names returns the names of the flag +func (f *IntSliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *IntSliceFlag) SetDestination(slice []int) func (f *IntSliceFlag) SetValue(slice []int) @@ -1531,6 +1565,9 @@ func (f *PathFlag) IsVisible() bool func (f *PathFlag) Names() []string Names returns the names of the flag +func (f *PathFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *PathFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1668,6 +1705,9 @@ func (f *StringFlag) IsVisible() bool func (f *StringFlag) Names() []string Names returns the names of the flag +func (f *StringFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *StringFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1755,6 +1795,9 @@ func (f *StringSliceFlag) IsVisible() bool func (f *StringSliceFlag) Names() []string Names returns the names of the flag +func (f *StringSliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *StringSliceFlag) SetDestination(slice []string) func (f *StringSliceFlag) SetValue(slice []string) @@ -1856,6 +1899,9 @@ func (f *TimestampFlag) IsVisible() bool func (f *TimestampFlag) Names() []string Names returns the names of the flag +func (f *TimestampFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *TimestampFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -1918,6 +1964,9 @@ func (f *Uint64Flag) IsVisible() bool func (f *Uint64Flag) Names() []string Names returns the names of the flag +func (f *Uint64Flag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Uint64Flag) String() string String returns a readable representation of this value (for usage defaults) @@ -2063,6 +2112,9 @@ func (f *UintFlag) IsVisible() bool func (f *UintFlag) Names() []string Names returns the names of the flag +func (f *UintFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *UintFlag) String() string String returns a readable representation of this value (for usage defaults) diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 034ef35dad..0309a28689 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -22,6 +22,8 @@ type Float64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []float64) error } // IsSet returns whether or not the flag has been set through env or file @@ -64,6 +66,8 @@ type GenericFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, interface{}) error } // String returns a readable representation of this value (for usage defaults) @@ -109,6 +113,8 @@ type Int64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []int64) error } // IsSet returns whether or not the flag has been set through env or file @@ -149,6 +155,8 @@ type IntSliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []int) error } // IsSet returns whether or not the flag has been set through env or file @@ -191,6 +199,8 @@ type PathFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, Path) error } // String returns a readable representation of this value (for usage defaults) @@ -238,6 +248,8 @@ type StringSliceFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, []string) error } // IsSet returns whether or not the flag has been set through env or file @@ -282,6 +294,8 @@ type TimestampFlag struct { Layout string Timezone *time.Location + + Action func(*Context, *time.Time) error } // String returns a readable representation of this value (for usage defaults) @@ -327,6 +341,8 @@ type Uint64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []uint64) error } // IsSet returns whether or not the flag has been set through env or file @@ -367,6 +383,8 @@ type UintSliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []uint) error } // IsSet returns whether or not the flag has been set through env or file @@ -409,6 +427,8 @@ type BoolFlag struct { EnvVars []string Count *int + + Action func(*Context, bool) error } // String returns a readable representation of this value (for usage defaults) @@ -454,6 +474,8 @@ type Float64Flag struct { Aliases []string EnvVars []string + + Action func(*Context, float64) error } // String returns a readable representation of this value (for usage defaults) @@ -501,6 +523,8 @@ type IntFlag struct { EnvVars []string Base int + + Action func(*Context, int) error } // String returns a readable representation of this value (for usage defaults) @@ -548,6 +572,8 @@ type Int64Flag struct { EnvVars []string Base int + + Action func(*Context, int64) error } // String returns a readable representation of this value (for usage defaults) @@ -595,6 +621,8 @@ type StringFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, string) error } // String returns a readable representation of this value (for usage defaults) @@ -640,6 +668,8 @@ type DurationFlag struct { Aliases []string EnvVars []string + + Action func(*Context, time.Duration) error } // String returns a readable representation of this value (for usage defaults) @@ -687,6 +717,8 @@ type UintFlag struct { EnvVars []string Base int + + Action func(*Context, uint) error } // String returns a readable representation of this value (for usage defaults) @@ -734,6 +766,8 @@ type Uint64Flag struct { EnvVars []string Base int + + Action func(*Context, uint64) error } // String returns a readable representation of this value (for usage defaults)