From 72981296081e25a2c81ee1145b9bc61bdb48e42c Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 1 Mar 2025 17:48:20 -0500 Subject: [PATCH 01/18] Test --- args.go | 11 +++++++++++ godoc-current.txt | 3 +++ 2 files changed, 14 insertions(+) diff --git a/args.go b/args.go index 280297bfb4..f849674450 100644 --- a/args.go +++ b/args.go @@ -64,6 +64,7 @@ func (a *stringSliceArgs) Slice() []string { type Argument interface { Parse([]string) ([]string, error) Usage() string + Get() any } // AnyArguments to differentiate between no arguments(nil) vs aleast one @@ -150,6 +151,16 @@ func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) { return s[count:], nil } +func (a *ArgumentBase[T, C, VC]) Get() any { + if a.Values == nil { + return nil + } + if a.Max == 1 { + return (*a.Values)[0] + } + return *a.Values +} + type ( FloatArg = ArgumentBase[float64, NoConfig, floatValue] IntArg = ArgumentBase[int, IntegerConfig, intValue[int]] diff --git a/godoc-current.txt b/godoc-current.txt index e62ba79f5b..4417ab1612 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -249,6 +249,7 @@ type Args interface { type Argument interface { Parse([]string) ([]string, error) Usage() string + Get() any } type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { @@ -262,6 +263,8 @@ type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { Config C `json:"config"` // config for this argument similar to Flag Config } +func (a *ArgumentBase[T, C, VC]) Get() any + func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) func (a *ArgumentBase[T, C, VC]) Usage() string From b6c5a56beaae1854c48fd93e9849a9852b0c1741 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Mon, 31 Mar 2025 07:13:35 -0400 Subject: [PATCH 02/18] Test --- args.go | 107 +++++++++++++++++++++++-------------- args_test.go | 58 ++++++++++---------- command_test.go | 2 +- godoc-current.txt | 132 ++++++++++------------------------------------ 4 files changed, 123 insertions(+), 176 deletions(-) diff --git a/args.go b/args.go index f849674450..c3b65933cd 100644 --- a/args.go +++ b/args.go @@ -62,6 +62,7 @@ func (a *stringSliceArgs) Slice() []string { } type Argument interface { + HasName(string) bool Parse([]string) ([]string, error) Usage() string Get() any @@ -69,23 +70,28 @@ type Argument interface { // AnyArguments to differentiate between no arguments(nil) vs aleast one var AnyArguments = []Argument{ - &StringArg{ + &StringArgs{ Max: -1, }, } -type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { +type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { Name string `json:"name"` // the name of this argument Value T `json:"value"` // the default value of this argument - Destination *T `json:"-"` // the destination point for this argument - Values *[]T `json:"-"` // all the values of this argument, only if multiple are supported + Destination *[]T `json:"-"` // the destination point for this argument UsageText string `json:"usageText"` // the usage text to show Min int `json:"minTimes"` // the min num of occurrences of this argument Max int `json:"maxTimes"` // the max num of occurrences of this argument, set to -1 for unlimited Config C `json:"config"` // config for this argument similar to Flag Config + + values []T +} + +func (a *ArgumentsBase[T, C, VC]) HasName(s string) bool { + return s == a.Name } -func (a *ArgumentBase[T, C, VC]) Usage() string { +func (a *ArgumentsBase[T, C, VC]) Usage() string { if a.UsageText != "" { return a.UsageText } @@ -103,7 +109,7 @@ func (a *ArgumentBase[T, C, VC]) Usage() string { return fmt.Sprintf(usageFormat, a.Name) } -func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) { +func (a *ArgumentsBase[T, C, VC]) Parse(s []string) ([]string, error) { tracef("calling arg%[1] parse with args %[2]", &a.Name, s) if a.Max == 0 { fmt.Printf("WARNING args %s has max 0, not parsing argument\n", a.Name) @@ -118,13 +124,13 @@ func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) { var vc VC var t T value := vc.Create(a.Value, &t, a.Config) - values := []T{} + a.values = []T{} for _, arg := range s { if err := value.Set(arg); err != nil { return s, err } - values = append(values, value.Get().(T)) + a.values = append(a.values, value.Get().(T)) count++ if count >= a.Max && a.Max > -1 { break @@ -134,46 +140,67 @@ func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) { return s, fmt.Errorf("sufficient count of arg %s not provided, given %d expected %d", a.Name, count, a.Min) } - if a.Values == nil { - a.Values = &values - } else if count > 0 { - *a.Values = values + if a.Destination != nil { + *a.Destination = a.values } - if a.Max == 1 && a.Destination != nil { - if len(values) > 0 { - *a.Destination = values[0] - } else { - *a.Destination = t + return s[count:], nil +} + +func (a *ArgumentsBase[T, C, VC]) Get() any { + return a.values +} + +type ( + FloatArgs = ArgumentsBase[float64, NoConfig, floatValue] + IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] + StringArgs = ArgumentsBase[string, StringConfig, stringValue] + StringMapArgs = ArgumentsBase[map[string]string, StringConfig, StringMap] + TimestampArgs = ArgumentsBase[time.Time, TimestampConfig, timestampValue] + UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] +) + +func (c *Command) StringArgs(name string) []string { + for _, arg := range c.Arguments { + if arg.HasName(name) { + return arg.Get().([]string) } } + return nil +} - return s[count:], nil +func (c *Command) FloatArgs(name string) []float64 { + for _, arg := range c.Arguments { + if arg.HasName(name) { + return arg.Get().([]float64) + } + } + return nil } -func (a *ArgumentBase[T, C, VC]) Get() any { - if a.Values == nil { - return nil +func (c *Command) IntArgs(name string) []int64 { + for _, arg := range c.Arguments { + if arg.HasName(name) { + return arg.Get().([]int64) + } } - if a.Max == 1 { - return (*a.Values)[0] + return nil +} + +func (c *Command) UintArgs(name string) []uint64 { + for _, arg := range c.Arguments { + if arg.HasName(name) { + return arg.Get().([]uint64) + } } - return *a.Values + return nil } -type ( - FloatArg = ArgumentBase[float64, NoConfig, floatValue] - IntArg = ArgumentBase[int, IntegerConfig, intValue[int]] - Int8Arg = ArgumentBase[int8, IntegerConfig, intValue[int8]] - Int16Arg = ArgumentBase[int16, IntegerConfig, intValue[int16]] - Int32Arg = ArgumentBase[int32, IntegerConfig, intValue[int32]] - Int64Arg = ArgumentBase[int64, IntegerConfig, intValue[int64]] - StringArg = ArgumentBase[string, StringConfig, stringValue] - StringMapArg = ArgumentBase[map[string]string, StringConfig, StringMap] - TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] - UintArg = ArgumentBase[uint, IntegerConfig, uintValue[uint]] - Uint8Arg = ArgumentBase[uint8, IntegerConfig, uintValue[uint8]] - Uint16Arg = ArgumentBase[uint16, IntegerConfig, uintValue[uint16]] - Uint32Arg = ArgumentBase[uint32, IntegerConfig, uintValue[uint32]] - Uint64Arg = ArgumentBase[uint64, IntegerConfig, uintValue[uint64]] -) +func (c *Command) TimestampArgs(name string) []time.Time { + for _, arg := range c.Arguments { + if arg.HasName(name) { + return arg.Get().([]time.Time) + } + } + return nil +} diff --git a/args_test.go b/args_test.go index b5267e2852..dc748b3649 100644 --- a/args_test.go +++ b/args_test.go @@ -11,45 +11,43 @@ import ( func TestArgumentsRootCommand(t *testing.T) { cmd := buildMinimalTestCommand() - var ival int64 - var fval float64 + var ivals []int64 var fvals []float64 cmd.Arguments = []Argument{ - &Int64Arg{ + &IntArgs{ Name: "ia", Min: 1, Max: 1, - Destination: &ival, + Destination: &ivals, }, - &FloatArg{ + &FloatArgs{ Name: "fa", Min: 0, Max: 2, - Destination: &fval, - Values: &fvals, + Destination: &fvals, }, } require.NoError(t, cmd.Run(context.Background(), []string{"foo", "10"})) - require.Equal(t, int64(10), ival) + require.Equal(t, []int64{10}, ivals) require.NoError(t, cmd.Run(context.Background(), []string{"foo", "12", "10.1"})) - require.Equal(t, int64(12), ival) + require.Equal(t, []int64{12}, ivals) require.Equal(t, []float64{10.1}, fvals) require.NoError(t, cmd.Run(context.Background(), []string{"foo", "13", "10.1", "11.09"})) - require.Equal(t, int64(13), ival) + require.Equal(t, []int64{13}, ivals) require.Equal(t, []float64{10.1, 11.09}, fvals) require.Error(t, errors.New("No help topic for '12.1"), cmd.Run(context.Background(), []string{"foo", "13", "10.1", "11.09", "12.1"})) - require.Equal(t, int64(13), ival) + require.Equal(t, []int64{13}, ivals) require.Equal(t, []float64{10.1, 11.09}, fvals) cmd.Arguments = append(cmd.Arguments, - &StringArg{ + &StringArgs{ Name: "sa", }, - &Uint64Arg{ + &UintArgs{ Name: "ua", Min: 2, Max: 1, // max is less than min @@ -63,7 +61,7 @@ func TestArgumentsSubcommand(t *testing.T) { cmd := buildMinimalTestCommand() var ifval int64 var svals []string - var tval time.Time + var tvals []time.Time cmd.Commands = []*Command{ { Name: "subcmd", @@ -75,20 +73,20 @@ func TestArgumentsSubcommand(t *testing.T) { }, }, Arguments: []Argument{ - &TimestampArg{ + &TimestampArgs{ Name: "ta", Min: 1, Max: 1, - Destination: &tval, + Destination: &tvals, Config: TimestampConfig{ Layouts: []string{time.RFC3339}, }, }, - &StringArg{ - Name: "sa", - Min: 1, - Max: 3, - Values: &svals, + &StringArgs{ + Name: "sa", + Min: 1, + Max: 3, + Destination: &svals, }, }, }, @@ -104,17 +102,17 @@ func TestArgumentsSubcommand(t *testing.T) { require.Equal(t, 1, numUsageErrors) require.NoError(t, cmd.Run(context.Background(), []string{"foo", "subcmd", "2006-01-02T15:04:05Z", "fubar"})) - require.Equal(t, time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC), tval) + require.Equal(t, []time.Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, tvals) require.Equal(t, []string{"fubar"}, svals) require.NoError(t, cmd.Run(context.Background(), []string{"foo", "subcmd", "--foo", "100", "2006-01-02T15:04:05Z", "fubar", "some"})) require.Equal(t, int64(100), ifval) - require.Equal(t, time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC), tval) + require.Equal(t, []time.Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, tvals) require.Equal(t, []string{"fubar", "some"}, svals) } func TestArgsUsage(t *testing.T) { - arg := &Int64Arg{ + arg := &IntArgs{ Name: "ia", Min: 0, Max: 1, @@ -180,8 +178,8 @@ func TestArgsUsage(t *testing.T) { func TestSingleOptionalArg(t *testing.T) { cmd := buildMinimalTestCommand() - var s1 string - arg := &StringArg{ + var s1 []string + arg := &StringArgs{ Min: 0, Max: 1, Destination: &s1, @@ -191,7 +189,7 @@ func TestSingleOptionalArg(t *testing.T) { } require.NoError(t, cmd.Run(context.Background(), []string{"foo"})) - require.Equal(t, "", s1) + require.Equal(t, []string{""}, s1) arg.Value = "bar" require.NoError(t, cmd.Run(context.Background(), []string{"foo"})) @@ -202,7 +200,7 @@ func TestSingleOptionalArg(t *testing.T) { } func TestUnboundedArgs(t *testing.T) { - arg := &StringArg{ + arg := &StringArgs{ Min: 0, Max: -1, } @@ -240,9 +238,9 @@ func TestUnboundedArgs(t *testing.T) { cmd.Arguments = []Argument{arg} for _, test := range tests { t.Run(test.name, func(t *testing.T) { - arg.Values = &test.values + arg.Destination = &test.values require.NoError(t, cmd.Run(context.Background(), test.args)) - require.Equal(t, test.expected, *arg.Values) + require.Equal(t, test.expected, *arg.Destination) }) } } diff --git a/command_test.go b/command_test.go index c0715ad9ea..152a5986ac 100644 --- a/command_test.go +++ b/command_test.go @@ -4501,7 +4501,7 @@ func TestSliceStringFlagParsing(t *testing.T) { func TestJSONExportCommand(t *testing.T) { cmd := buildExtendedTestCommand() cmd.Arguments = []Argument{ - &Int64Arg{ + &IntArgs{ Name: "fooi", }, } diff --git a/godoc-current.txt b/godoc-current.txt index 4417ab1612..b08f630dff 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -46,7 +46,7 @@ var ( SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate ) var AnyArguments = []Argument{ - &StringArg{ + &StringArgs{ Max: -1, }, } @@ -247,27 +247,31 @@ type Args interface { } type Argument interface { + HasName(string) bool Parse([]string) ([]string, error) Usage() string Get() any } -type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { +type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { Name string `json:"name"` // the name of this argument Value T `json:"value"` // the default value of this argument - Destination *T `json:"-"` // the destination point for this argument - Values *[]T `json:"-"` // all the values of this argument, only if multiple are supported + Destination *[]T `json:"-"` // the destination point for this argument UsageText string `json:"usageText"` // the usage text to show Min int `json:"minTimes"` // the min num of occurrences of this argument Max int `json:"maxTimes"` // the max num of occurrences of this argument, set to -1 for unlimited Config C `json:"config"` // config for this argument similar to Flag Config + + // Has unexported fields. } -func (a *ArgumentBase[T, C, VC]) Get() any +func (a *ArgumentsBase[T, C, VC]) Get() any + +func (a *ArgumentsBase[T, C, VC]) HasName(s string) bool -func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) +func (a *ArgumentsBase[T, C, VC]) Parse(s []string) ([]string, error) -func (a *ArgumentBase[T, C, VC]) Usage() string +func (a *ArgumentsBase[T, C, VC]) Usage() string type BeforeFunc func(context.Context, *Command) (context.Context, error) BeforeFunc is an action that executes prior to any subcommands being run @@ -497,6 +501,8 @@ func (cmd *Command) FlagNames() []string func (cmd *Command) Float(name string) float64 Float looks up the value of a local FloatFlag, returns 0 if not found +func (c *Command) FloatArgs(name string) []float64 + func (cmd *Command) FloatSlice(name string) []float64 FloatSlice looks up the value of a local FloatSliceFlag, returns nil if not found @@ -514,35 +520,9 @@ func (cmd *Command) HasName(name string) bool func (cmd *Command) Int(name string) int Int looks up the value of a local Int64Flag, returns 0 if not found -func (cmd *Command) Int16(name string) int16 - Int16 looks up the value of a local Int16Flag, returns 0 if not found - -func (cmd *Command) Int16Slice(name string) []int16 - Int16Slice looks up the value of a local Int16SliceFlag, returns nil if not - found - -func (cmd *Command) Int32(name string) int32 - Int32 looks up the value of a local Int32Flag, returns 0 if not found - -func (cmd *Command) Int32Slice(name string) []int32 - Int32Slice looks up the value of a local Int32SliceFlag, returns nil if not - found - -func (cmd *Command) Int64(name string) int64 - Int64 looks up the value of a local Int64Flag, returns 0 if not found - -func (cmd *Command) Int64Slice(name string) []int64 - Int64Slice looks up the value of a local Int64SliceFlag, returns nil if not - found +func (c *Command) IntArgs(name string) []int64 -func (cmd *Command) Int8(name string) int8 - Int8 looks up the value of a local Int8Flag, returns 0 if not found - -func (cmd *Command) Int8Slice(name string) []int8 - Int8Slice looks up the value of a local Int8SliceFlag, returns nil if not - found - -func (cmd *Command) IntSlice(name string) []int +func (cmd *Command) IntSlice(name string) []int64 IntSlice looks up the value of a local IntSliceFlag, returns nil if not found @@ -578,6 +558,8 @@ func (cmd *Command) Set(name, value string) error func (cmd *Command) String(name string) string +func (c *Command) StringArgs(name string) []string + func (cmd *Command) StringMap(name string) map[string]string StringMap looks up the value of a local StringMapFlag, returns nil if not found @@ -589,6 +571,8 @@ func (cmd *Command) StringSlice(name string) []string func (cmd *Command) Timestamp(name string) time.Time Timestamp gets the timestamp from a flag name +func (c *Command) TimestampArgs(name string) []time.Time + func (cmd *Command) ToFishCompletion() (string, error) ToFishCompletion creates a fish completion string for the `*App` The function errors if either parsing or writing of the string fails. @@ -596,35 +580,9 @@ func (cmd *Command) ToFishCompletion() (string, error) func (cmd *Command) Uint(name string) uint Uint looks up the value of a local Uint64Flag, returns 0 if not found -func (cmd *Command) Uint16(name string) uint16 - Uint16 looks up the value of a local Uint16Flag, returns 0 if not found - -func (cmd *Command) Uint16Slice(name string) []uint16 - Uint16Slice looks up the value of a local Uint16SliceFlag, returns nil if - not found - -func (cmd *Command) Uint32(name string) uint32 - Uint32 looks up the value of a local Uint32Flag, returns 0 if not found - -func (cmd *Command) Uint32Slice(name string) []uint32 - Uint32Slice looks up the value of a local Uint32SliceFlag, returns nil if - not found - -func (cmd *Command) Uint64(name string) uint64 - Uint64 looks up the value of a local Uint64Flag, returns 0 if not found +func (c *Command) UintArgs(name string) []uint64 -func (cmd *Command) Uint64Slice(name string) []uint64 - Uint64Slice looks up the value of a local Uint64SliceFlag, returns nil if - not found - -func (cmd *Command) Uint8(name string) uint8 - Uint8 looks up the value of a local Uint8Flag, returns 0 if not found - -func (cmd *Command) Uint8Slice(name string) []uint8 - Uint8Slice looks up the value of a local Uint8SliceFlag, returns nil if not - found - -func (cmd *Command) UintSlice(name string) []uint +func (cmd *Command) UintSlice(name string) []uint64 UintSlice looks up the value of a local UintSliceFlag, returns nil if not found @@ -940,7 +898,7 @@ func (f FlagsByName) Less(i, j int) bool func (f FlagsByName) Swap(i, j int) -type FloatArg = ArgumentBase[float64, NoConfig, floatValue] +type FloatArgs = ArgumentsBase[float64, NoConfig, floatValue] type FloatFlag = FlagBase[float64, NoConfig, floatValue] @@ -950,11 +908,7 @@ type FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice] type GenericFlag = FlagBase[Value, NoConfig, genericValue] -type Int16Arg = ArgumentBase[int16, IntegerConfig, intValue[int16]] - -type Int16Flag = FlagBase[int16, IntegerConfig, intValue[int16]] - -type Int16Slice = SliceBase[int16, IntegerConfig, intValue[int16]] +type IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] type Int16SliceFlag = FlagBase[[]int16, IntegerConfig, Int16Slice] @@ -1119,7 +1073,7 @@ func (i SliceBase[T, C, VC]) ToString(t []T) string func (i *SliceBase[T, C, VC]) Value() []T Value returns the slice of values set by this flag -type StringArg = ArgumentBase[string, StringConfig, stringValue] +type StringArgs = ArgumentsBase[string, StringConfig, stringValue] type StringConfig struct { // Whether to trim whitespace of parsed value @@ -1131,7 +1085,7 @@ type StringFlag = FlagBase[string, StringConfig, stringValue] type StringMap = MapBase[string, StringConfig, stringValue] -type StringMapArg = ArgumentBase[map[string]string, StringConfig, StringMap] +type StringMapArgs = ArgumentsBase[map[string]string, StringConfig, StringMap] type StringMapFlag = FlagBase[map[string]string, StringConfig, StringMap] @@ -1143,7 +1097,7 @@ type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string -type TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] +type TimestampArgs = ArgumentsBase[time.Time, TimestampConfig, timestampValue] type TimestampConfig struct { Timezone *time.Location @@ -1158,39 +1112,7 @@ type TimestampConfig struct { type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue] -type Uint16Arg = ArgumentBase[uint16, IntegerConfig, uintValue[uint16]] - -type Uint16Flag = FlagBase[uint16, IntegerConfig, uintValue[uint16]] - -type Uint16Slice = SliceBase[uint16, IntegerConfig, uintValue[uint16]] - -type Uint16SliceFlag = FlagBase[[]uint16, IntegerConfig, Uint16Slice] - -type Uint32Arg = ArgumentBase[uint32, IntegerConfig, uintValue[uint32]] - -type Uint32Flag = FlagBase[uint32, IntegerConfig, uintValue[uint32]] - -type Uint32Slice = SliceBase[uint32, IntegerConfig, uintValue[uint32]] - -type Uint32SliceFlag = FlagBase[[]uint32, IntegerConfig, Uint32Slice] - -type Uint64Arg = ArgumentBase[uint64, IntegerConfig, uintValue[uint64]] - -type Uint64Flag = FlagBase[uint64, IntegerConfig, uintValue[uint64]] - -type Uint64Slice = SliceBase[uint64, IntegerConfig, uintValue[uint64]] - -type Uint64SliceFlag = FlagBase[[]uint64, IntegerConfig, Uint64Slice] - -type Uint8Arg = ArgumentBase[uint8, IntegerConfig, uintValue[uint8]] - -type Uint8Flag = FlagBase[uint8, IntegerConfig, uintValue[uint8]] - -type Uint8Slice = SliceBase[uint8, IntegerConfig, uintValue[uint8]] - -type Uint8SliceFlag = FlagBase[[]uint8, IntegerConfig, Uint8Slice] - -type UintArg = ArgumentBase[uint, IntegerConfig, uintValue[uint]] +type UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] type UintFlag = FlagBase[uint, IntegerConfig, uintValue[uint]] From 09bd22ed9b9c2ea1eb5b18177620e607b6328af6 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Tue, 1 Apr 2025 09:47:44 -0400 Subject: [PATCH 03/18] Test --- args.go | 44 ++++++++++++--- args_test.go | 133 +++++++++++++++++++++++++++++++--------------- godoc-current.txt | 8 +++ 3 files changed, 135 insertions(+), 50 deletions(-) diff --git a/args.go b/args.go index c3b65933cd..3118fb8caa 100644 --- a/args.go +++ b/args.go @@ -61,10 +61,19 @@ func (a *stringSliceArgs) Slice() []string { return ret } +// Argument captures a positional argument that can +// be parsed type Argument interface { + // which this argument can be accessed using the given name HasName(string) bool + + // Parse the given args and return unparsed args and/or error Parse([]string) ([]string, error) + + // The usage template for this argument to use in help Usage() string + + // The Value of this Arg Get() any } @@ -126,10 +135,12 @@ func (a *ArgumentsBase[T, C, VC]) Parse(s []string) ([]string, error) { value := vc.Create(a.Value, &t, a.Config) a.values = []T{} + tracef("attempting arg%[1] parse", &a.Name) for _, arg := range s { if err := value.Set(arg); err != nil { return s, err } + tracef("set arg%[1] one value", &a.Name, value.Get().(T)) a.values = append(a.values, value.Get().(T)) count++ if count >= a.Max && a.Max > -1 { @@ -141,7 +152,8 @@ func (a *ArgumentsBase[T, C, VC]) Parse(s []string) ([]string, error) { } if a.Destination != nil { - *a.Destination = a.values + tracef("appending destination") + *a.Destination = append(*a.Destination, a.values...) } return s[count:], nil @@ -163,7 +175,11 @@ type ( func (c *Command) StringArgs(name string) []string { for _, arg := range c.Arguments { if arg.HasName(name) { - return arg.Get().([]string) + if a, ok := arg.Get().([]string); ok { + return a + } else { + return nil + } } } return nil @@ -172,7 +188,11 @@ func (c *Command) StringArgs(name string) []string { func (c *Command) FloatArgs(name string) []float64 { for _, arg := range c.Arguments { if arg.HasName(name) { - return arg.Get().([]float64) + if a, ok := arg.Get().([]float64); ok { + return a + } else { + return nil + } } } return nil @@ -181,7 +201,11 @@ func (c *Command) FloatArgs(name string) []float64 { func (c *Command) IntArgs(name string) []int64 { for _, arg := range c.Arguments { if arg.HasName(name) { - return arg.Get().([]int64) + if a, ok := arg.Get().([]int64); ok { + return a + } else { + return nil + } } } return nil @@ -190,7 +214,11 @@ func (c *Command) IntArgs(name string) []int64 { func (c *Command) UintArgs(name string) []uint64 { for _, arg := range c.Arguments { if arg.HasName(name) { - return arg.Get().([]uint64) + if a, ok := arg.Get().([]uint64); ok { + return a + } else { + return nil + } } } return nil @@ -199,7 +227,11 @@ func (c *Command) UintArgs(name string) []uint64 { func (c *Command) TimestampArgs(name string) []time.Time { for _, arg := range c.Arguments { if arg.HasName(name) { - return arg.Get().([]time.Time) + if a, ok := arg.Get().([]time.Time); ok { + return a + } else { + return nil + } } } return nil diff --git a/args_test.go b/args_test.go index dc748b3649..319297b0e4 100644 --- a/args_test.go +++ b/args_test.go @@ -10,51 +10,93 @@ import ( ) func TestArgumentsRootCommand(t *testing.T) { - cmd := buildMinimalTestCommand() - var ivals []int64 - var fvals []float64 - cmd.Arguments = []Argument{ - &IntArgs{ - Name: "ia", - Min: 1, - Max: 1, - Destination: &ivals, - }, - &FloatArgs{ - Name: "fa", - Min: 0, - Max: 2, - Destination: &fvals, + tests := []struct { + name string + args []string + expectedIvals []int64 + expectedFvals []float64 + errStr string + }{ + { + name: "set ival", + args: []string{"foo", "10"}, + expectedIvals: []int64{10}, + }, + { + name: "set ival fval", + args: []string{"foo", "12", "10.1"}, + expectedIvals: []int64{12}, + expectedFvals: []float64{10.1}, + }, + { + name: "set ival multu fvals", + args: []string{"foo", "13", "10.1", "11.09"}, + expectedIvals: []int64{13}, + expectedFvals: []float64{10.1, 11.09}, + }, + { + name: "set fvals beyond max", + args: []string{"foo", "13", "10.1", "11.09", "12.1"}, + expectedIvals: []int64{13}, + expectedFvals: []float64{10.1, 11.09}, + errStr: "No help topic for '12.1'", }, } - require.NoError(t, cmd.Run(context.Background(), []string{"foo", "10"})) - require.Equal(t, []int64{10}, ivals) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cmd := buildMinimalTestCommand() + var ivals []int64 + var fvals []float64 + cmd.Arguments = []Argument{ + &IntArgs{ + Name: "ia", + Min: 1, + Max: 1, + Destination: &ivals, + }, + &FloatArgs{ + Name: "fa", + Min: 0, + Max: 2, + Destination: &fvals, + }, + } + + err := cmd.Run(buildTestContext(t), test.args) - require.NoError(t, cmd.Run(context.Background(), []string{"foo", "12", "10.1"})) - require.Equal(t, []int64{12}, ivals) - require.Equal(t, []float64{10.1}, fvals) + r := require.New(t) - require.NoError(t, cmd.Run(context.Background(), []string{"foo", "13", "10.1", "11.09"})) - require.Equal(t, []int64{13}, ivals) - require.Equal(t, []float64{10.1, 11.09}, fvals) + if test.errStr != "" { + r.ErrorContains(err, test.errStr) + } + r.Equal(test.expectedIvals, ivals) + r.Equal(test.expectedIvals, cmd.IntArgs("ia")) + r.Equal(test.expectedFvals, fvals) + if test.expectedFvals != nil { + r.Equal(test.expectedFvals, cmd.FloatArgs("fa")) + } else { + r.Equal([]float64{}, cmd.FloatArgs("fa")) + } + }) + } - require.Error(t, errors.New("No help topic for '12.1"), cmd.Run(context.Background(), []string{"foo", "13", "10.1", "11.09", "12.1"})) - require.Equal(t, []int64{13}, ivals) - require.Equal(t, []float64{10.1, 11.09}, fvals) + /* + cmd.Arguments = append(cmd.Arguments, - cmd.Arguments = append(cmd.Arguments, - &StringArgs{ - Name: "sa", - }, - &UintArgs{ - Name: "ua", - Min: 2, - Max: 1, // max is less than min - }, - ) + &StringArgs{ + Name: "sa", + }, + &UintArgs{ + Name: "ua", + Min: 2, + Max: 1, // max is less than min + }, + + ) - require.NoError(t, cmd.Run(context.Background(), []string{"foo", "10"})) + require.NoError(t, cmd.Run(context.Background(), []string{"foo", "10"})) + */ } func TestArgumentsSubcommand(t *testing.T) { @@ -101,10 +143,13 @@ func TestArgumentsSubcommand(t *testing.T) { require.Error(t, errors.New("sufficient count of arg sa not provided, given 0 expected 1"), cmd.Run(context.Background(), []string{"foo", "subcmd", "2006-01-02T15:04:05Z"})) require.Equal(t, 1, numUsageErrors) + tvals = []time.Time{} require.NoError(t, cmd.Run(context.Background(), []string{"foo", "subcmd", "2006-01-02T15:04:05Z", "fubar"})) require.Equal(t, []time.Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, tvals) require.Equal(t, []string{"fubar"}, svals) + tvals = []time.Time{} + svals = []string{} require.NoError(t, cmd.Run(context.Background(), []string{"foo", "subcmd", "--foo", "100", "2006-01-02T15:04:05Z", "fubar", "some"})) require.Equal(t, int64(100), ifval) require.Equal(t, []time.Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, tvals) @@ -189,14 +234,14 @@ func TestSingleOptionalArg(t *testing.T) { } require.NoError(t, cmd.Run(context.Background(), []string{"foo"})) - require.Equal(t, []string{""}, s1) + require.Equal(t, []string{}, s1) - arg.Value = "bar" + /*arg.Value = "bar" require.NoError(t, cmd.Run(context.Background(), []string{"foo"})) - require.Equal(t, "bar", s1) + require.Equal(t, "bar", s1)*/ require.NoError(t, cmd.Run(context.Background(), []string{"foo", "zbar"})) - require.Equal(t, "zbar", s1) + require.Equal(t, []string{"zbar"}, s1) } func TestUnboundedArgs(t *testing.T) { @@ -213,7 +258,7 @@ func TestUnboundedArgs(t *testing.T) { { name: "cmd accepts no args", args: []string{"foo"}, - expected: nil, + expected: []string{}, }, { name: "cmd uses given args", @@ -234,10 +279,10 @@ func TestUnboundedArgs(t *testing.T) { }, } - cmd := buildMinimalTestCommand() - cmd.Arguments = []Argument{arg} for _, test := range tests { t.Run(test.name, func(t *testing.T) { + cmd := buildMinimalTestCommand() + cmd.Arguments = []Argument{arg} arg.Destination = &test.values require.NoError(t, cmd.Run(context.Background(), test.args)) require.Equal(t, test.expected, *arg.Destination) diff --git a/godoc-current.txt b/godoc-current.txt index b08f630dff..d1aef11f80 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -247,11 +247,19 @@ type Args interface { } type Argument interface { + // which this argument can be accessed using the given name HasName(string) bool + + // Parse the given args and return unparsed args and/or error Parse([]string) ([]string, error) + + // The usage template for this argument to use in help Usage() string + + // The Value of this Arg Get() any } + Argument captures a positional argument that can be parsed type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { Name string `json:"name"` // the name of this argument From 381fee973446925fbd7baef1ade39dd5ed68bf66 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Tue, 1 Apr 2025 21:00:24 -0400 Subject: [PATCH 04/18] Update tests --- args_test.go | 187 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 65 deletions(-) diff --git a/args_test.go b/args_test.go index 319297b0e4..39ef1fb232 100644 --- a/args_test.go +++ b/args_test.go @@ -2,7 +2,6 @@ package cli import ( "context" - "errors" "testing" "time" @@ -100,60 +99,95 @@ func TestArgumentsRootCommand(t *testing.T) { } func TestArgumentsSubcommand(t *testing.T) { - cmd := buildMinimalTestCommand() - var ifval int64 - var svals []string - var tvals []time.Time - cmd.Commands = []*Command{ + tests := []struct { + name string + args []string + expectedIval int64 + expectedSvals []string + expectedTVals []time.Time + errStr string + }{ { - Name: "subcmd", - Flags: []Flag{ - &Int64Flag{ - Name: "foo", - Value: 10, - Destination: &ifval, - }, - }, - Arguments: []Argument{ - &TimestampArgs{ - Name: "ta", - Min: 1, - Max: 1, - Destination: &tvals, - Config: TimestampConfig{ - Layouts: []string{time.RFC3339}, - }, - }, - &StringArgs{ - Name: "sa", - Min: 1, - Max: 3, - Destination: &svals, - }, - }, + name: "insuff args", + args: []string{"foo", "subcmd", "2006-01-02T15:04:05Z"}, + errStr: "sufficient count of arg sa not provided, given 0 expected 1", + }, + { + name: "set sval and tval", + args: []string{"foo", "subcmd", "2006-01-02T15:04:05Z", "fubar"}, + expectedIval: 10, + expectedTVals: []time.Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, + expectedSvals: []string{"fubar"}, + }, + { + name: "set sval, tval and ival", + args: []string{"foo", "subcmd", "--foo", "100", "2006-01-02T15:04:05Z", "fubar", "some"}, + expectedIval: 100, + expectedTVals: []time.Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, + expectedSvals: []string{"fubar", "some"}, }, } - numUsageErrors := 0 - cmd.Commands[0].OnUsageError = func(ctx context.Context, cmd *Command, err error, isSubcommand bool) error { - numUsageErrors++ - return err - } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cmd := buildMinimalTestCommand() + var ival int64 + var svals []string + var tvals []time.Time + cmd.Commands = []*Command{ + { + Name: "subcmd", + Flags: []Flag{ + &IntFlag{ + Name: "foo", + Value: 10, + Destination: &ival, + }, + }, + Arguments: []Argument{ + &TimestampArgs{ + Name: "ta", + Min: 1, + Max: 1, + Destination: &tvals, + Config: TimestampConfig{ + Layouts: []string{time.RFC3339}, + }, + }, + &StringArgs{ + Name: "sa", + Min: 1, + Max: 3, + Destination: &svals, + }, + }, + }, + } - require.Error(t, errors.New("sufficient count of arg sa not provided, given 0 expected 1"), cmd.Run(context.Background(), []string{"foo", "subcmd", "2006-01-02T15:04:05Z"})) - require.Equal(t, 1, numUsageErrors) + numUsageErrors := 0 + cmd.Commands[0].OnUsageError = func(ctx context.Context, cmd *Command, err error, isSubcommand bool) error { + numUsageErrors++ + return err + } + + err := cmd.Run(buildTestContext(t), test.args) - tvals = []time.Time{} - require.NoError(t, cmd.Run(context.Background(), []string{"foo", "subcmd", "2006-01-02T15:04:05Z", "fubar"})) - require.Equal(t, []time.Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, tvals) - require.Equal(t, []string{"fubar"}, svals) + r := require.New(t) - tvals = []time.Time{} - svals = []string{} - require.NoError(t, cmd.Run(context.Background(), []string{"foo", "subcmd", "--foo", "100", "2006-01-02T15:04:05Z", "fubar", "some"})) - require.Equal(t, int64(100), ifval) - require.Equal(t, []time.Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, tvals) - require.Equal(t, []string{"fubar", "some"}, svals) + if test.errStr != "" { + r.ErrorContains(err, test.errStr) + r.Equal(1, numUsageErrors) + } else { + if test.expectedSvals != nil { + r.Equal(test.expectedSvals, svals) + } + if test.expectedTVals != nil { + r.Equal(test.expectedTVals, tvals) + } + r.Equal(test.expectedIval, ival) + } + }) + } } func TestArgsUsage(t *testing.T) { @@ -222,26 +256,49 @@ func TestArgsUsage(t *testing.T) { } func TestSingleOptionalArg(t *testing.T) { - cmd := buildMinimalTestCommand() - var s1 []string - arg := &StringArgs{ - Min: 0, - Max: 1, - Destination: &s1, - } - cmd.Arguments = []Argument{ - arg, + tests := []struct { + name string + args []string + argValue string + exp []string + }{ + { + name: "no args", + args: []string{"foo"}, + exp: nil, + }, + /*{ + name: "no arg with def value", + args: []string{"foo"}, + exp: []string{"bar"}, + },*/ + { + name: "one arg", + args: []string{"foo", "zbar"}, + exp: []string{"zbar"}, + }, } - require.NoError(t, cmd.Run(context.Background(), []string{"foo"})) - require.Equal(t, []string{}, s1) - - /*arg.Value = "bar" - require.NoError(t, cmd.Run(context.Background(), []string{"foo"})) - require.Equal(t, "bar", s1)*/ + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cmd := buildMinimalTestCommand() + var s1 []string + arg := &StringArgs{ + Min: 0, + Max: 1, + Value: test.argValue, + Destination: &s1, + } + cmd.Arguments = []Argument{ + arg, + } - require.NoError(t, cmd.Run(context.Background(), []string{"foo", "zbar"})) - require.Equal(t, []string{"zbar"}, s1) + err := cmd.Run(buildTestContext(t), test.args) // + r := require.New(t) + r.NoError(err) + r.Equal(test.exp, s1) + }) + } } func TestUnboundedArgs(t *testing.T) { From e48034a2bae30b21444667b9fec716be9d0f6d4f Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 6 Apr 2025 18:23:05 -0400 Subject: [PATCH 05/18] Fix tests --- args.go | 2 +- args_test.go | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/args.go b/args.go index 3118fb8caa..9434a530cb 100644 --- a/args.go +++ b/args.go @@ -153,7 +153,7 @@ func (a *ArgumentsBase[T, C, VC]) Parse(s []string) ([]string, error) { if a.Destination != nil { tracef("appending destination") - *a.Destination = append(*a.Destination, a.values...) + *a.Destination = a.values // append(*a.Destination, a.values...) } return s[count:], nil diff --git a/args_test.go b/args_test.go index 39ef1fb232..7ebc4b0f87 100644 --- a/args_test.go +++ b/args_test.go @@ -20,6 +20,7 @@ func TestArgumentsRootCommand(t *testing.T) { name: "set ival", args: []string{"foo", "10"}, expectedIvals: []int64{10}, + expectedFvals: []float64{}, }, { name: "set ival fval", @@ -265,7 +266,7 @@ func TestSingleOptionalArg(t *testing.T) { { name: "no args", args: []string{"foo"}, - exp: nil, + exp: []string{}, }, /*{ name: "no arg with def value", @@ -307,10 +308,11 @@ func TestUnboundedArgs(t *testing.T) { Max: -1, } tests := []struct { - name string - args []string - values []string - expected []string + name string + args []string + defValues []string + values []string + expected []string }{ { name: "cmd accepts no args", @@ -325,8 +327,7 @@ func TestUnboundedArgs(t *testing.T) { { name: "cmd uses default values", args: []string{"foo"}, - values: []string{"zbar", "zbaz"}, - expected: []string{"zbar", "zbaz"}, + expected: []string{}, }, { name: "given args override default values", From d402c72fb5f28b50c606631fa2fe25da6abd8945 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Mon, 7 Apr 2025 09:05:59 -0400 Subject: [PATCH 06/18] Run make v3approve --- testdata/godoc-v3.x.txt | 141 +++++++++++----------------------------- 1 file changed, 37 insertions(+), 104 deletions(-) diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index e62ba79f5b..d1aef11f80 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -46,7 +46,7 @@ var ( SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate ) var AnyArguments = []Argument{ - &StringArg{ + &StringArgs{ Max: -1, }, } @@ -247,24 +247,39 @@ type Args interface { } type Argument interface { + // which this argument can be accessed using the given name + HasName(string) bool + + // Parse the given args and return unparsed args and/or error Parse([]string) ([]string, error) + + // The usage template for this argument to use in help Usage() string + + // The Value of this Arg + Get() any } + Argument captures a positional argument that can be parsed -type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { +type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { Name string `json:"name"` // the name of this argument Value T `json:"value"` // the default value of this argument - Destination *T `json:"-"` // the destination point for this argument - Values *[]T `json:"-"` // all the values of this argument, only if multiple are supported + Destination *[]T `json:"-"` // the destination point for this argument UsageText string `json:"usageText"` // the usage text to show Min int `json:"minTimes"` // the min num of occurrences of this argument Max int `json:"maxTimes"` // the max num of occurrences of this argument, set to -1 for unlimited Config C `json:"config"` // config for this argument similar to Flag Config + + // Has unexported fields. } -func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) +func (a *ArgumentsBase[T, C, VC]) Get() any + +func (a *ArgumentsBase[T, C, VC]) HasName(s string) bool -func (a *ArgumentBase[T, C, VC]) Usage() string +func (a *ArgumentsBase[T, C, VC]) Parse(s []string) ([]string, error) + +func (a *ArgumentsBase[T, C, VC]) Usage() string type BeforeFunc func(context.Context, *Command) (context.Context, error) BeforeFunc is an action that executes prior to any subcommands being run @@ -494,6 +509,8 @@ func (cmd *Command) FlagNames() []string func (cmd *Command) Float(name string) float64 Float looks up the value of a local FloatFlag, returns 0 if not found +func (c *Command) FloatArgs(name string) []float64 + func (cmd *Command) FloatSlice(name string) []float64 FloatSlice looks up the value of a local FloatSliceFlag, returns nil if not found @@ -511,35 +528,9 @@ func (cmd *Command) HasName(name string) bool func (cmd *Command) Int(name string) int Int looks up the value of a local Int64Flag, returns 0 if not found -func (cmd *Command) Int16(name string) int16 - Int16 looks up the value of a local Int16Flag, returns 0 if not found - -func (cmd *Command) Int16Slice(name string) []int16 - Int16Slice looks up the value of a local Int16SliceFlag, returns nil if not - found - -func (cmd *Command) Int32(name string) int32 - Int32 looks up the value of a local Int32Flag, returns 0 if not found - -func (cmd *Command) Int32Slice(name string) []int32 - Int32Slice looks up the value of a local Int32SliceFlag, returns nil if not - found - -func (cmd *Command) Int64(name string) int64 - Int64 looks up the value of a local Int64Flag, returns 0 if not found - -func (cmd *Command) Int64Slice(name string) []int64 - Int64Slice looks up the value of a local Int64SliceFlag, returns nil if not - found - -func (cmd *Command) Int8(name string) int8 - Int8 looks up the value of a local Int8Flag, returns 0 if not found - -func (cmd *Command) Int8Slice(name string) []int8 - Int8Slice looks up the value of a local Int8SliceFlag, returns nil if not - found +func (c *Command) IntArgs(name string) []int64 -func (cmd *Command) IntSlice(name string) []int +func (cmd *Command) IntSlice(name string) []int64 IntSlice looks up the value of a local IntSliceFlag, returns nil if not found @@ -575,6 +566,8 @@ func (cmd *Command) Set(name, value string) error func (cmd *Command) String(name string) string +func (c *Command) StringArgs(name string) []string + func (cmd *Command) StringMap(name string) map[string]string StringMap looks up the value of a local StringMapFlag, returns nil if not found @@ -586,6 +579,8 @@ func (cmd *Command) StringSlice(name string) []string func (cmd *Command) Timestamp(name string) time.Time Timestamp gets the timestamp from a flag name +func (c *Command) TimestampArgs(name string) []time.Time + func (cmd *Command) ToFishCompletion() (string, error) ToFishCompletion creates a fish completion string for the `*App` The function errors if either parsing or writing of the string fails. @@ -593,35 +588,9 @@ func (cmd *Command) ToFishCompletion() (string, error) func (cmd *Command) Uint(name string) uint Uint looks up the value of a local Uint64Flag, returns 0 if not found -func (cmd *Command) Uint16(name string) uint16 - Uint16 looks up the value of a local Uint16Flag, returns 0 if not found - -func (cmd *Command) Uint16Slice(name string) []uint16 - Uint16Slice looks up the value of a local Uint16SliceFlag, returns nil if - not found - -func (cmd *Command) Uint32(name string) uint32 - Uint32 looks up the value of a local Uint32Flag, returns 0 if not found +func (c *Command) UintArgs(name string) []uint64 -func (cmd *Command) Uint32Slice(name string) []uint32 - Uint32Slice looks up the value of a local Uint32SliceFlag, returns nil if - not found - -func (cmd *Command) Uint64(name string) uint64 - Uint64 looks up the value of a local Uint64Flag, returns 0 if not found - -func (cmd *Command) Uint64Slice(name string) []uint64 - Uint64Slice looks up the value of a local Uint64SliceFlag, returns nil if - not found - -func (cmd *Command) Uint8(name string) uint8 - Uint8 looks up the value of a local Uint8Flag, returns 0 if not found - -func (cmd *Command) Uint8Slice(name string) []uint8 - Uint8Slice looks up the value of a local Uint8SliceFlag, returns nil if not - found - -func (cmd *Command) UintSlice(name string) []uint +func (cmd *Command) UintSlice(name string) []uint64 UintSlice looks up the value of a local UintSliceFlag, returns nil if not found @@ -937,7 +906,7 @@ func (f FlagsByName) Less(i, j int) bool func (f FlagsByName) Swap(i, j int) -type FloatArg = ArgumentBase[float64, NoConfig, floatValue] +type FloatArgs = ArgumentsBase[float64, NoConfig, floatValue] type FloatFlag = FlagBase[float64, NoConfig, floatValue] @@ -947,11 +916,7 @@ type FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice] type GenericFlag = FlagBase[Value, NoConfig, genericValue] -type Int16Arg = ArgumentBase[int16, IntegerConfig, intValue[int16]] - -type Int16Flag = FlagBase[int16, IntegerConfig, intValue[int16]] - -type Int16Slice = SliceBase[int16, IntegerConfig, intValue[int16]] +type IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] type Int16SliceFlag = FlagBase[[]int16, IntegerConfig, Int16Slice] @@ -1116,7 +1081,7 @@ func (i SliceBase[T, C, VC]) ToString(t []T) string func (i *SliceBase[T, C, VC]) Value() []T Value returns the slice of values set by this flag -type StringArg = ArgumentBase[string, StringConfig, stringValue] +type StringArgs = ArgumentsBase[string, StringConfig, stringValue] type StringConfig struct { // Whether to trim whitespace of parsed value @@ -1128,7 +1093,7 @@ type StringFlag = FlagBase[string, StringConfig, stringValue] type StringMap = MapBase[string, StringConfig, stringValue] -type StringMapArg = ArgumentBase[map[string]string, StringConfig, StringMap] +type StringMapArgs = ArgumentsBase[map[string]string, StringConfig, StringMap] type StringMapFlag = FlagBase[map[string]string, StringConfig, StringMap] @@ -1140,7 +1105,7 @@ type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string -type TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] +type TimestampArgs = ArgumentsBase[time.Time, TimestampConfig, timestampValue] type TimestampConfig struct { Timezone *time.Location @@ -1155,39 +1120,7 @@ type TimestampConfig struct { type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue] -type Uint16Arg = ArgumentBase[uint16, IntegerConfig, uintValue[uint16]] - -type Uint16Flag = FlagBase[uint16, IntegerConfig, uintValue[uint16]] - -type Uint16Slice = SliceBase[uint16, IntegerConfig, uintValue[uint16]] - -type Uint16SliceFlag = FlagBase[[]uint16, IntegerConfig, Uint16Slice] - -type Uint32Arg = ArgumentBase[uint32, IntegerConfig, uintValue[uint32]] - -type Uint32Flag = FlagBase[uint32, IntegerConfig, uintValue[uint32]] - -type Uint32Slice = SliceBase[uint32, IntegerConfig, uintValue[uint32]] - -type Uint32SliceFlag = FlagBase[[]uint32, IntegerConfig, Uint32Slice] - -type Uint64Arg = ArgumentBase[uint64, IntegerConfig, uintValue[uint64]] - -type Uint64Flag = FlagBase[uint64, IntegerConfig, uintValue[uint64]] - -type Uint64Slice = SliceBase[uint64, IntegerConfig, uintValue[uint64]] - -type Uint64SliceFlag = FlagBase[[]uint64, IntegerConfig, Uint64Slice] - -type Uint8Arg = ArgumentBase[uint8, IntegerConfig, uintValue[uint8]] - -type Uint8Flag = FlagBase[uint8, IntegerConfig, uintValue[uint8]] - -type Uint8Slice = SliceBase[uint8, IntegerConfig, uintValue[uint8]] - -type Uint8SliceFlag = FlagBase[[]uint8, IntegerConfig, Uint8Slice] - -type UintArg = ArgumentBase[uint, IntegerConfig, uintValue[uint]] +type UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] type UintFlag = FlagBase[uint, IntegerConfig, uintValue[uint]] From 21885239550de31e3d10d16f1c5a2e02844a9149 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 13 Apr 2025 16:59:20 -0400 Subject: [PATCH 07/18] Fix more tests --- args.go | 68 ++++++++++++++++++++++------------------------------ args_test.go | 66 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 72 insertions(+), 62 deletions(-) diff --git a/args.go b/args.go index 9434a530cb..71cac6b154 100644 --- a/args.go +++ b/args.go @@ -160,7 +160,10 @@ func (a *ArgumentsBase[T, C, VC]) Parse(s []string) ([]string, error) { } func (a *ArgumentsBase[T, C, VC]) Get() any { - return a.values + if a.values != nil { + return a.values + } + return []T{} } type ( @@ -172,67 +175,54 @@ type ( UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] ) -func (c *Command) StringArgs(name string) []string { +func (c *Command) getArgValue(name string) any { + tracef("command %s looking for args %s", c.Name, name) for _, arg := range c.Arguments { if arg.HasName(name) { - if a, ok := arg.Get().([]string); ok { - return a - } else { - return nil - } + tracef("command %s found args %s", c.Name, name) + return arg.Get() } } + tracef("command %s did not find args %s", c.Name, name) + return nil +} + +func (c *Command) StringArgs(name string) []string { + val := c.getArgValue(name) + if a, ok := val.([]string); ok { + return a + } return nil } func (c *Command) FloatArgs(name string) []float64 { - for _, arg := range c.Arguments { - if arg.HasName(name) { - if a, ok := arg.Get().([]float64); ok { - return a - } else { - return nil - } - } + val := c.getArgValue(name) + if a, ok := val.([]float64); ok { + return a } return nil } func (c *Command) IntArgs(name string) []int64 { - for _, arg := range c.Arguments { - if arg.HasName(name) { - if a, ok := arg.Get().([]int64); ok { - return a - } else { - return nil - } - } + val := c.getArgValue(name) + if a, ok := val.([]int64); ok { + return a } return nil } func (c *Command) UintArgs(name string) []uint64 { - for _, arg := range c.Arguments { - if arg.HasName(name) { - if a, ok := arg.Get().([]uint64); ok { - return a - } else { - return nil - } - } + val := c.getArgValue(name) + if a, ok := val.([]uint64); ok { + return a } return nil } func (c *Command) TimestampArgs(name string) []time.Time { - for _, arg := range c.Arguments { - if arg.HasName(name) { - if a, ok := arg.Get().([]time.Time); ok { - return a - } else { - return nil - } - } + val := c.getArgValue(name) + if a, ok := val.([]time.Time); ok { + return a } return nil } diff --git a/args_test.go b/args_test.go index 7ebc4b0f87..be486e1cd4 100644 --- a/args_test.go +++ b/args_test.go @@ -10,36 +10,47 @@ import ( func TestArgumentsRootCommand(t *testing.T) { tests := []struct { - name string - args []string - expectedIvals []int64 - expectedFvals []float64 - errStr string + name string + args []string + expectedIvals []int64 + expectedUivals []uint64 + expectedFvals []float64 + errStr string }{ { - name: "set ival", - args: []string{"foo", "10"}, - expectedIvals: []int64{10}, - expectedFvals: []float64{}, + name: "set ival", + args: []string{"foo", "10"}, + expectedIvals: []int64{10}, + expectedUivals: []uint64{}, + }, + { + name: "set ival uival", + args: []string{"foo", "-10", "11"}, + expectedIvals: []int64{-10}, + expectedUivals: []uint64{11}, + expectedFvals: []float64{}, }, { - name: "set ival fval", - args: []string{"foo", "12", "10.1"}, - expectedIvals: []int64{12}, - expectedFvals: []float64{10.1}, + name: "set ival uival fval", + args: []string{"foo", "-12", "14", "10.1"}, + expectedIvals: []int64{-12}, + expectedUivals: []uint64{14}, + expectedFvals: []float64{10.1}, }, { - name: "set ival multu fvals", - args: []string{"foo", "13", "10.1", "11.09"}, - expectedIvals: []int64{13}, - expectedFvals: []float64{10.1, 11.09}, + name: "set ival uival multu fvals", + args: []string{"foo", "-13", "12", "10.1", "11.09"}, + expectedIvals: []int64{-13}, + expectedUivals: []uint64{12}, + expectedFvals: []float64{10.1, 11.09}, }, { - name: "set fvals beyond max", - args: []string{"foo", "13", "10.1", "11.09", "12.1"}, - expectedIvals: []int64{13}, - expectedFvals: []float64{10.1, 11.09}, - errStr: "No help topic for '12.1'", + name: "set fvals beyond max", + args: []string{"foo", "13", "10", "10.1", "11.09", "12.1"}, + expectedIvals: []int64{13}, + expectedUivals: []uint64{10}, + expectedFvals: []float64{10.1, 11.09}, + errStr: "No help topic for '12.1'", }, } @@ -47,6 +58,7 @@ func TestArgumentsRootCommand(t *testing.T) { t.Run(test.name, func(t *testing.T) { cmd := buildMinimalTestCommand() var ivals []int64 + var uivals []uint64 var fvals []float64 cmd.Arguments = []Argument{ &IntArgs{ @@ -55,6 +67,12 @@ func TestArgumentsRootCommand(t *testing.T) { Max: 1, Destination: &ivals, }, + &UintArgs{ + Name: "uia", + Min: 1, + Max: 1, + Destination: &uivals, + }, &FloatArgs{ Name: "fa", Min: 0, @@ -72,8 +90,8 @@ func TestArgumentsRootCommand(t *testing.T) { } r.Equal(test.expectedIvals, ivals) r.Equal(test.expectedIvals, cmd.IntArgs("ia")) - r.Equal(test.expectedFvals, fvals) if test.expectedFvals != nil { + r.Equal(test.expectedFvals, fvals) r.Equal(test.expectedFvals, cmd.FloatArgs("fa")) } else { r.Equal([]float64{}, cmd.FloatArgs("fa")) @@ -181,9 +199,11 @@ func TestArgumentsSubcommand(t *testing.T) { } else { if test.expectedSvals != nil { r.Equal(test.expectedSvals, svals) + r.Equal(test.expectedSvals, cmd.Commands[0].StringArgs("sa")) } if test.expectedTVals != nil { r.Equal(test.expectedTVals, tvals) + r.Equal(test.expectedTVals, cmd.Commands[0].TimestampArgs("ta")) } r.Equal(test.expectedIval, ival) } From 2daf572bd6b7526487f13fa00e245196a5b97d5b Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 13 Apr 2025 19:04:16 -0400 Subject: [PATCH 08/18] Fix tests --- args.go | 1 + args_test.go | 25 ++++++++++++++++++++----- command_parse.go | 6 ++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/args.go b/args.go index 71cac6b154..3458d7d4a4 100644 --- a/args.go +++ b/args.go @@ -197,6 +197,7 @@ func (c *Command) StringArgs(name string) []string { func (c *Command) FloatArgs(name string) []float64 { val := c.getArgValue(name) + tracef("command %s found args %s %v", c.Name, name, val) if a, ok := val.([]float64); ok { return a } diff --git a/args_test.go b/args_test.go index be486e1cd4..0894850b57 100644 --- a/args_test.go +++ b/args_test.go @@ -22,6 +22,7 @@ func TestArgumentsRootCommand(t *testing.T) { args: []string{"foo", "10"}, expectedIvals: []int64{10}, expectedUivals: []uint64{}, + expectedFvals: []float64{}, }, { name: "set ival uival", @@ -90,12 +91,10 @@ func TestArgumentsRootCommand(t *testing.T) { } r.Equal(test.expectedIvals, ivals) r.Equal(test.expectedIvals, cmd.IntArgs("ia")) - if test.expectedFvals != nil { + r.Equal(test.expectedFvals, cmd.FloatArgs("fa")) + /*if test.expectedFvals != nil { r.Equal(test.expectedFvals, fvals) - r.Equal(test.expectedFvals, cmd.FloatArgs("fa")) - } else { - r.Equal([]float64{}, cmd.FloatArgs("fa")) - } + }*/ }) } @@ -117,6 +116,22 @@ func TestArgumentsRootCommand(t *testing.T) { */ } +func TestArgumentsInvalidType(t *testing.T) { + cmd := buildMinimalTestCommand() + cmd.Arguments = []Argument{ + &IntArgs{ + Name: "ia", + Min: 1, + Max: 1, + }, + } + r := require.New(t) + r.Nil(cmd.StringArgs("ia")) + r.Nil(cmd.FloatArgs("ia")) + r.Nil(cmd.UintArgs("ia")) + r.Nil(cmd.TimestampArgs("ia")) +} + func TestArgumentsSubcommand(t *testing.T) { tests := []struct { name string diff --git a/command_parse.go b/command_parse.go index 15fb8de4fe..1130658005 100644 --- a/command_parse.go +++ b/command_parse.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "strings" + "unicode" ) const ( @@ -117,6 +118,11 @@ func (cmd *Command) parseFlags(args Args) (Args, error) { if firstArg[1] == '-' { numMinuses++ shortOptionHandling = false + } else if !unicode.IsLetter(rune(firstArg[1])) { + // this is not a flag + tracef("parseFlags not a unicode letter. Stop parsing") + posArgs = append(posArgs, rargs...) + return &stringSliceArgs{posArgs}, nil } tracef("parseFlags (shortOptionHandling=%[1]q)", shortOptionHandling) From 79302c98eb476111939ab86665f39f337d03c109 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 13 Apr 2025 19:16:27 -0400 Subject: [PATCH 09/18] Add coverage --- args_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/args_test.go b/args_test.go index 0894850b57..46df6b314b 100644 --- a/args_test.go +++ b/args_test.go @@ -92,6 +92,7 @@ func TestArgumentsRootCommand(t *testing.T) { r.Equal(test.expectedIvals, ivals) r.Equal(test.expectedIvals, cmd.IntArgs("ia")) r.Equal(test.expectedFvals, cmd.FloatArgs("fa")) + r.Equal(test.expectedUivals, cmd.UintArgs("uia")) /*if test.expectedFvals != nil { r.Equal(test.expectedFvals, fvals) }*/ @@ -130,6 +131,7 @@ func TestArgumentsInvalidType(t *testing.T) { r.Nil(cmd.FloatArgs("ia")) r.Nil(cmd.UintArgs("ia")) r.Nil(cmd.TimestampArgs("ia")) + r.Nil(cmd.IntArgs("uia")) } func TestArgumentsSubcommand(t *testing.T) { From 547f669cb4791a1f396d5183c34aa0e59d1644e6 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 13 Apr 2025 19:23:23 -0400 Subject: [PATCH 10/18] Add coverage --- args_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/args_test.go b/args_test.go index 46df6b314b..7771922202 100644 --- a/args_test.go +++ b/args_test.go @@ -24,6 +24,14 @@ func TestArgumentsRootCommand(t *testing.T) { expectedUivals: []uint64{}, expectedFvals: []float64{}, }, + { + name: "set invalid ival", + args: []string{"foo", "10.0"}, + expectedIvals: []int64{}, + expectedUivals: []uint64{}, + expectedFvals: []float64{}, + errStr: "strconv.ParseInt: parsing \"10.0\": invalid syntax", + }, { name: "set ival uival", args: []string{"foo", "-10", "11"}, @@ -88,8 +96,9 @@ func TestArgumentsRootCommand(t *testing.T) { if test.errStr != "" { r.ErrorContains(err, test.errStr) + } else { + r.Equal(test.expectedIvals, ivals) } - r.Equal(test.expectedIvals, ivals) r.Equal(test.expectedIvals, cmd.IntArgs("ia")) r.Equal(test.expectedFvals, cmd.FloatArgs("fa")) r.Equal(test.expectedUivals, cmd.UintArgs("uia")) From 97ed291312a183a134a80277f7d99fa446badb7c Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Tue, 15 Apr 2025 15:59:05 -0400 Subject: [PATCH 11/18] Add more args --- args.go | 108 +++++++++++++++++++++++++++++++++++++++- args_test.go | 23 ++++----- godoc-current.txt | 41 ++++++++++++++- testdata/godoc-v3.x.txt | 41 ++++++++++++++- 4 files changed, 197 insertions(+), 16 deletions(-) diff --git a/args.go b/args.go index 3458d7d4a4..bd096ac2c3 100644 --- a/args.go +++ b/args.go @@ -84,6 +84,65 @@ var AnyArguments = []Argument{ }, } +type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { + Name string `json:"name"` // the name of this argument + Value T `json:"value"` // the default value of this argument + Destination *T `json:"-"` // the destination point for this argument + UsageText string `json:"usageText"` // the usage text to show + Config C `json:"config"` // config for this argument similar to Flag Config + + value *T +} + +func (a *ArgumentBase[T, C, VC]) HasName(s string) bool { + return s == a.Name +} + +func (a *ArgumentBase[T, C, VC]) Usage() string { + if a.UsageText != "" { + return a.UsageText + } + + usageFormat := "%[1]s" + return fmt.Sprintf(usageFormat, a.Name) +} + +func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) { + tracef("calling arg%[1] parse with args %[2]", a.Name, s) + + var vc VC + var t T + value := vc.Create(a.Value, &t, a.Config) + a.value = &t + + tracef("attempting arg%[1] parse", &a.Name) + if len(s) > 0 { + if err := value.Set(s[0]); err != nil { + return s, err + } + *a.value = value.Get().(T) + tracef("set arg%[1] one value", a.Name, *a.value) + } + + if a.Destination != nil { + tracef("setting destination") + *a.Destination = *a.value + } + + if len(s) > 0 { + return s[1:], nil + } + return s, nil +} + +func (a *ArgumentBase[T, C, VC]) Get() any { + if a.value != nil { + return *a.value + } + return a.Value +} + +// ArgumentsBase is a base type for slice arguments type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { Name string `json:"name"` // the name of this argument Value T `json:"value"` // the default value of this argument @@ -167,10 +226,16 @@ func (a *ArgumentsBase[T, C, VC]) Get() any { } type ( + FloatArg = ArgumentBase[float64, NoConfig, floatValue] + IntArg = ArgumentBase[int64, IntegerConfig, intValue] + StringArg = ArgumentBase[string, StringConfig, stringValue] + StringMapArgs = ArgumentBase[map[string]string, StringConfig, StringMap] + TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] + UintArg = ArgumentBase[uint64, IntegerConfig, uintValue] + FloatArgs = ArgumentsBase[float64, NoConfig, floatValue] IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] StringArgs = ArgumentsBase[string, StringConfig, stringValue] - StringMapArgs = ArgumentsBase[map[string]string, StringConfig, StringMap] TimestampArgs = ArgumentsBase[time.Time, TimestampConfig, timestampValue] UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] ) @@ -187,6 +252,14 @@ func (c *Command) getArgValue(name string) any { return nil } +func (c *Command) StringArg(name string) string { + val := c.getArgValue(name) + if a, ok := val.(string); ok { + return a + } + return "" +} + func (c *Command) StringArgs(name string) []string { val := c.getArgValue(name) if a, ok := val.([]string); ok { @@ -195,15 +268,30 @@ func (c *Command) StringArgs(name string) []string { return nil } +func (c *Command) FloatArg(name string) float64 { + val := c.getArgValue(name) + if a, ok := val.(float64); ok { + return a + } + return 0 +} + func (c *Command) FloatArgs(name string) []float64 { val := c.getArgValue(name) - tracef("command %s found args %s %v", c.Name, name, val) if a, ok := val.([]float64); ok { return a } return nil } +func (c *Command) IntArg(name string) int64 { + val := c.getArgValue(name) + if a, ok := val.(int64); ok { + return a + } + return 0 +} + func (c *Command) IntArgs(name string) []int64 { val := c.getArgValue(name) if a, ok := val.([]int64); ok { @@ -212,6 +300,14 @@ func (c *Command) IntArgs(name string) []int64 { return nil } +func (c *Command) UintArg(name string) uint64 { + val := c.getArgValue(name) + if a, ok := val.(uint64); ok { + return a + } + return 0 +} + func (c *Command) UintArgs(name string) []uint64 { val := c.getArgValue(name) if a, ok := val.([]uint64); ok { @@ -220,6 +316,14 @@ func (c *Command) UintArgs(name string) []uint64 { return nil } +func (c *Command) TimestampArg(name string) time.Time { + val := c.getArgValue(name) + if a, ok := val.(time.Time); ok { + return a + } + return time.Time{} +} + func (c *Command) TimestampArgs(name string) []time.Time { val := c.getArgValue(name) if a, ok := val.([]time.Time); ok { diff --git a/args_test.go b/args_test.go index 7771922202..9cc414c0ec 100644 --- a/args_test.go +++ b/args_test.go @@ -307,32 +307,31 @@ func TestSingleOptionalArg(t *testing.T) { name string args []string argValue string - exp []string + exp string }{ { name: "no args", args: []string{"foo"}, - exp: []string{}, + exp: "", + }, + { + name: "no arg with def value", + args: []string{"foo"}, + argValue: "bar", + exp: "bar", }, - /*{ - name: "no arg with def value", - args: []string{"foo"}, - exp: []string{"bar"}, - },*/ { name: "one arg", args: []string{"foo", "zbar"}, - exp: []string{"zbar"}, + exp: "zbar", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { cmd := buildMinimalTestCommand() - var s1 []string - arg := &StringArgs{ - Min: 0, - Max: 1, + var s1 string + arg := &StringArg{ Value: test.argValue, Destination: &s1, } diff --git a/godoc-current.txt b/godoc-current.txt index d1aef11f80..250dadc12e 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -261,6 +261,24 @@ type Argument interface { } Argument captures a positional argument that can be parsed +type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { + Name string `json:"name"` // the name of this argument + Value T `json:"value"` // the default value of this argument + Destination *T `json:"-"` // the destination point for this argument + UsageText string `json:"usageText"` // the usage text to show + Config C `json:"config"` // config for this argument similar to Flag Config + + // Has unexported fields. +} + +func (a *ArgumentBase[T, C, VC]) Get() any + +func (a *ArgumentBase[T, C, VC]) HasName(s string) bool + +func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) + +func (a *ArgumentBase[T, C, VC]) Usage() string + type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { Name string `json:"name"` // the name of this argument Value T `json:"value"` // the default value of this argument @@ -272,6 +290,7 @@ type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { // Has unexported fields. } + ArgumentsBase is a base type for slice arguments func (a *ArgumentsBase[T, C, VC]) Get() any @@ -509,6 +528,8 @@ func (cmd *Command) FlagNames() []string func (cmd *Command) Float(name string) float64 Float looks up the value of a local FloatFlag, returns 0 if not found +func (c *Command) FloatArg(name string) float64 + func (c *Command) FloatArgs(name string) []float64 func (cmd *Command) FloatSlice(name string) []float64 @@ -528,6 +549,8 @@ func (cmd *Command) HasName(name string) bool func (cmd *Command) Int(name string) int Int looks up the value of a local Int64Flag, returns 0 if not found +func (c *Command) IntArg(name string) int64 + func (c *Command) IntArgs(name string) []int64 func (cmd *Command) IntSlice(name string) []int64 @@ -566,6 +589,8 @@ func (cmd *Command) Set(name, value string) error func (cmd *Command) String(name string) string +func (c *Command) StringArg(name string) string + func (c *Command) StringArgs(name string) []string func (cmd *Command) StringMap(name string) map[string]string @@ -579,6 +604,8 @@ func (cmd *Command) StringSlice(name string) []string func (cmd *Command) Timestamp(name string) time.Time Timestamp gets the timestamp from a flag name +func (c *Command) TimestampArg(name string) time.Time + func (c *Command) TimestampArgs(name string) []time.Time func (cmd *Command) ToFishCompletion() (string, error) @@ -588,6 +615,8 @@ func (cmd *Command) ToFishCompletion() (string, error) func (cmd *Command) Uint(name string) uint Uint looks up the value of a local Uint64Flag, returns 0 if not found +func (c *Command) UintArg(name string) uint64 + func (c *Command) UintArgs(name string) []uint64 func (cmd *Command) UintSlice(name string) []uint64 @@ -906,6 +935,8 @@ func (f FlagsByName) Less(i, j int) bool func (f FlagsByName) Swap(i, j int) +type FloatArg = ArgumentBase[float64, NoConfig, floatValue] + type FloatArgs = ArgumentsBase[float64, NoConfig, floatValue] type FloatFlag = FlagBase[float64, NoConfig, floatValue] @@ -916,6 +947,8 @@ type FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice] type GenericFlag = FlagBase[Value, NoConfig, genericValue] +type IntArg = ArgumentBase[int64, IntegerConfig, intValue] + type IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] type Int16SliceFlag = FlagBase[[]int16, IntegerConfig, Int16Slice] @@ -1081,6 +1114,8 @@ func (i SliceBase[T, C, VC]) ToString(t []T) string func (i *SliceBase[T, C, VC]) Value() []T Value returns the slice of values set by this flag +type StringArg = ArgumentBase[string, StringConfig, stringValue] + type StringArgs = ArgumentsBase[string, StringConfig, stringValue] type StringConfig struct { @@ -1093,7 +1128,7 @@ type StringFlag = FlagBase[string, StringConfig, stringValue] type StringMap = MapBase[string, StringConfig, stringValue] -type StringMapArgs = ArgumentsBase[map[string]string, StringConfig, StringMap] +type StringMapArgs = ArgumentBase[map[string]string, StringConfig, StringMap] type StringMapFlag = FlagBase[map[string]string, StringConfig, StringMap] @@ -1105,6 +1140,8 @@ type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string +type TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] + type TimestampArgs = ArgumentsBase[time.Time, TimestampConfig, timestampValue] type TimestampConfig struct { @@ -1120,6 +1157,8 @@ type TimestampConfig struct { type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue] +type UintArg = ArgumentBase[uint64, IntegerConfig, uintValue] + type UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] type UintFlag = FlagBase[uint, IntegerConfig, uintValue[uint]] diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index d1aef11f80..250dadc12e 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -261,6 +261,24 @@ type Argument interface { } Argument captures a positional argument that can be parsed +type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { + Name string `json:"name"` // the name of this argument + Value T `json:"value"` // the default value of this argument + Destination *T `json:"-"` // the destination point for this argument + UsageText string `json:"usageText"` // the usage text to show + Config C `json:"config"` // config for this argument similar to Flag Config + + // Has unexported fields. +} + +func (a *ArgumentBase[T, C, VC]) Get() any + +func (a *ArgumentBase[T, C, VC]) HasName(s string) bool + +func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) + +func (a *ArgumentBase[T, C, VC]) Usage() string + type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { Name string `json:"name"` // the name of this argument Value T `json:"value"` // the default value of this argument @@ -272,6 +290,7 @@ type ArgumentsBase[T any, C any, VC ValueCreator[T, C]] struct { // Has unexported fields. } + ArgumentsBase is a base type for slice arguments func (a *ArgumentsBase[T, C, VC]) Get() any @@ -509,6 +528,8 @@ func (cmd *Command) FlagNames() []string func (cmd *Command) Float(name string) float64 Float looks up the value of a local FloatFlag, returns 0 if not found +func (c *Command) FloatArg(name string) float64 + func (c *Command) FloatArgs(name string) []float64 func (cmd *Command) FloatSlice(name string) []float64 @@ -528,6 +549,8 @@ func (cmd *Command) HasName(name string) bool func (cmd *Command) Int(name string) int Int looks up the value of a local Int64Flag, returns 0 if not found +func (c *Command) IntArg(name string) int64 + func (c *Command) IntArgs(name string) []int64 func (cmd *Command) IntSlice(name string) []int64 @@ -566,6 +589,8 @@ func (cmd *Command) Set(name, value string) error func (cmd *Command) String(name string) string +func (c *Command) StringArg(name string) string + func (c *Command) StringArgs(name string) []string func (cmd *Command) StringMap(name string) map[string]string @@ -579,6 +604,8 @@ func (cmd *Command) StringSlice(name string) []string func (cmd *Command) Timestamp(name string) time.Time Timestamp gets the timestamp from a flag name +func (c *Command) TimestampArg(name string) time.Time + func (c *Command) TimestampArgs(name string) []time.Time func (cmd *Command) ToFishCompletion() (string, error) @@ -588,6 +615,8 @@ func (cmd *Command) ToFishCompletion() (string, error) func (cmd *Command) Uint(name string) uint Uint looks up the value of a local Uint64Flag, returns 0 if not found +func (c *Command) UintArg(name string) uint64 + func (c *Command) UintArgs(name string) []uint64 func (cmd *Command) UintSlice(name string) []uint64 @@ -906,6 +935,8 @@ func (f FlagsByName) Less(i, j int) bool func (f FlagsByName) Swap(i, j int) +type FloatArg = ArgumentBase[float64, NoConfig, floatValue] + type FloatArgs = ArgumentsBase[float64, NoConfig, floatValue] type FloatFlag = FlagBase[float64, NoConfig, floatValue] @@ -916,6 +947,8 @@ type FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice] type GenericFlag = FlagBase[Value, NoConfig, genericValue] +type IntArg = ArgumentBase[int64, IntegerConfig, intValue] + type IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] type Int16SliceFlag = FlagBase[[]int16, IntegerConfig, Int16Slice] @@ -1081,6 +1114,8 @@ func (i SliceBase[T, C, VC]) ToString(t []T) string func (i *SliceBase[T, C, VC]) Value() []T Value returns the slice of values set by this flag +type StringArg = ArgumentBase[string, StringConfig, stringValue] + type StringArgs = ArgumentsBase[string, StringConfig, stringValue] type StringConfig struct { @@ -1093,7 +1128,7 @@ type StringFlag = FlagBase[string, StringConfig, stringValue] type StringMap = MapBase[string, StringConfig, stringValue] -type StringMapArgs = ArgumentsBase[map[string]string, StringConfig, StringMap] +type StringMapArgs = ArgumentBase[map[string]string, StringConfig, StringMap] type StringMapFlag = FlagBase[map[string]string, StringConfig, StringMap] @@ -1105,6 +1140,8 @@ type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string +type TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] + type TimestampArgs = ArgumentsBase[time.Time, TimestampConfig, timestampValue] type TimestampConfig struct { @@ -1120,6 +1157,8 @@ type TimestampConfig struct { type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue] +type UintArg = ArgumentBase[uint64, IntegerConfig, uintValue] + type UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] type UintFlag = FlagBase[uint, IntegerConfig, uintValue[uint]] From 30c5a98dd2ca632588185b7acfd91bacb753dc75 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 19 Apr 2025 16:30:59 -0600 Subject: [PATCH 12/18] Add support to different int/uint types --- args.go | 161 ++++++++++++++++++++++++++++++---------------- args_test.go | 36 +++++------ godoc-current.txt | 160 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 273 insertions(+), 84 deletions(-) diff --git a/args.go b/args.go index bd096ac2c3..3a24206348 100644 --- a/args.go +++ b/args.go @@ -227,17 +227,33 @@ func (a *ArgumentsBase[T, C, VC]) Get() any { type ( FloatArg = ArgumentBase[float64, NoConfig, floatValue] - IntArg = ArgumentBase[int64, IntegerConfig, intValue] + IntArg = ArgumentBase[int, IntegerConfig, intValue[int]] + Int8Arg = ArgumentBase[int8, IntegerConfig, intValue[int8]] + Int16Arg = ArgumentBase[int16, IntegerConfig, intValue[int16]] + Int32Arg = ArgumentBase[int32, IntegerConfig, intValue[int32]] + Int64Arg = ArgumentBase[int64, IntegerConfig, intValue[int64]] StringArg = ArgumentBase[string, StringConfig, stringValue] StringMapArgs = ArgumentBase[map[string]string, StringConfig, StringMap] TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] - UintArg = ArgumentBase[uint64, IntegerConfig, uintValue] + UintArg = ArgumentBase[uint, IntegerConfig, uintValue[uint]] + Uint8Arg = ArgumentBase[uint8, IntegerConfig, uintValue[uint8]] + Uint16Arg = ArgumentBase[uint16, IntegerConfig, uintValue[uint16]] + Uint32Arg = ArgumentBase[uint32, IntegerConfig, uintValue[uint32]] + Uint64Arg = ArgumentBase[uint64, IntegerConfig, uintValue[uint64]] FloatArgs = ArgumentsBase[float64, NoConfig, floatValue] - IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] + IntArgs = ArgumentsBase[int, IntegerConfig, intValue[int]] + Int8Args = ArgumentsBase[int8, IntegerConfig, intValue[int8]] + Int16Args = ArgumentsBase[int16, IntegerConfig, intValue[int16]] + Int32Args = ArgumentsBase[int32, IntegerConfig, intValue[int32]] + Int64Args = ArgumentsBase[int64, IntegerConfig, intValue[int64]] StringArgs = ArgumentsBase[string, StringConfig, stringValue] TimestampArgs = ArgumentsBase[time.Time, TimestampConfig, timestampValue] - UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] + UintArgs = ArgumentsBase[uint, IntegerConfig, uintValue[uint]] + Uint8Args = ArgumentsBase[uint8, IntegerConfig, uintValue[uint8]] + Uint16Args = ArgumentsBase[uint16, IntegerConfig, uintValue[uint16]] + Uint32Args = ArgumentsBase[uint32, IntegerConfig, uintValue[uint32]] + Uint64Args = ArgumentsBase[uint64, IntegerConfig, uintValue[uint64]] ) func (c *Command) getArgValue(name string) any { @@ -252,82 +268,115 @@ func (c *Command) getArgValue(name string) any { return nil } -func (c *Command) StringArg(name string) string { +func arg[T any](name string, c *Command) T { val := c.getArgValue(name) - if a, ok := val.(string); ok { + if a, ok := val.(T); ok { return a } - return "" + var zero T + return zero +} + +func (c *Command) StringArg(name string) string { + return arg[string](name, c) } func (c *Command) StringArgs(name string) []string { - val := c.getArgValue(name) - if a, ok := val.([]string); ok { - return a - } - return nil + return arg[[]string](name, c) } func (c *Command) FloatArg(name string) float64 { - val := c.getArgValue(name) - if a, ok := val.(float64); ok { - return a - } - return 0 + return arg[float64](name, c) } func (c *Command) FloatArgs(name string) []float64 { - val := c.getArgValue(name) - if a, ok := val.([]float64); ok { - return a - } - return nil + return arg[[]float64](name, c) } -func (c *Command) IntArg(name string) int64 { - val := c.getArgValue(name) - if a, ok := val.(int64); ok { - return a - } - return 0 +func (c *Command) IntArg(name string) int { + return arg[int](name, c) } -func (c *Command) IntArgs(name string) []int64 { - val := c.getArgValue(name) - if a, ok := val.([]int64); ok { - return a - } - return nil +func (c *Command) IntArgs(name string) []int { + return arg[[]int](name, c) } -func (c *Command) UintArg(name string) uint64 { - val := c.getArgValue(name) - if a, ok := val.(uint64); ok { - return a - } - return 0 +func (c *Command) Int8Arg(name string) int8 { + return arg[int8](name, c) } -func (c *Command) UintArgs(name string) []uint64 { - val := c.getArgValue(name) - if a, ok := val.([]uint64); ok { - return a - } - return nil +func (c *Command) Int8Args(name string) []int8 { + return arg[[]int8](name, c) +} + +func (c *Command) Int16Arg(name string) int16 { + return arg[int16](name, c) +} + +func (c *Command) Int16Args(name string) []int16 { + return arg[[]int16](name, c) +} + +func (c *Command) Int32Arg(name string) int32 { + return arg[int32](name, c) +} + +func (c *Command) Int32Args(name string) []int32 { + return arg[[]int32](name, c) +} + +func (c *Command) Int64Arg(name string) int64 { + return arg[int64](name, c) +} + +func (c *Command) Int64Args(name string) []int64 { + return arg[[]int64](name, c) +} + +func (c *Command) UintArg(name string) uint { + return arg[uint](name, c) +} + +func (c *Command) Uint8Arg(name string) uint8 { + return arg[uint8](name, c) +} + +func (c *Command) Uint16Arg(name string) uint16 { + return arg[uint16](name, c) +} + +func (c *Command) Uint32Arg(name string) uint32 { + return arg[uint32](name, c) +} + +func (c *Command) Uint64Arg(name string) uint64 { + return arg[uint64](name, c) +} + +func (c *Command) UintArgs(name string) []uint { + return arg[[]uint](name, c) +} + +func (c *Command) Uint8Args(name string) []uint8 { + return arg[[]uint8](name, c) +} + +func (c *Command) Uint16Args(name string) []uint16 { + return arg[[]uint16](name, c) +} + +func (c *Command) Uint32Args(name string) []uint32 { + return arg[[]uint32](name, c) +} + +func (c *Command) Uint64Args(name string) []uint64 { + return arg[[]uint64](name, c) } func (c *Command) TimestampArg(name string) time.Time { - val := c.getArgValue(name) - if a, ok := val.(time.Time); ok { - return a - } - return time.Time{} + return arg[time.Time](name, c) } func (c *Command) TimestampArgs(name string) []time.Time { - val := c.getArgValue(name) - if a, ok := val.([]time.Time); ok { - return a - } - return nil + return arg[[]time.Time](name, c) } diff --git a/args_test.go b/args_test.go index 9cc414c0ec..944382fe46 100644 --- a/args_test.go +++ b/args_test.go @@ -12,52 +12,52 @@ func TestArgumentsRootCommand(t *testing.T) { tests := []struct { name string args []string - expectedIvals []int64 - expectedUivals []uint64 + expectedIvals []int + expectedUivals []uint expectedFvals []float64 errStr string }{ { name: "set ival", args: []string{"foo", "10"}, - expectedIvals: []int64{10}, - expectedUivals: []uint64{}, + expectedIvals: []int{10}, + expectedUivals: []uint{}, expectedFvals: []float64{}, }, { name: "set invalid ival", args: []string{"foo", "10.0"}, - expectedIvals: []int64{}, - expectedUivals: []uint64{}, + expectedIvals: []int{}, + expectedUivals: []uint{}, expectedFvals: []float64{}, errStr: "strconv.ParseInt: parsing \"10.0\": invalid syntax", }, { name: "set ival uival", args: []string{"foo", "-10", "11"}, - expectedIvals: []int64{-10}, - expectedUivals: []uint64{11}, + expectedIvals: []int{-10}, + expectedUivals: []uint{11}, expectedFvals: []float64{}, }, { name: "set ival uival fval", args: []string{"foo", "-12", "14", "10.1"}, - expectedIvals: []int64{-12}, - expectedUivals: []uint64{14}, + expectedIvals: []int{-12}, + expectedUivals: []uint{14}, expectedFvals: []float64{10.1}, }, { name: "set ival uival multu fvals", args: []string{"foo", "-13", "12", "10.1", "11.09"}, - expectedIvals: []int64{-13}, - expectedUivals: []uint64{12}, + expectedIvals: []int{-13}, + expectedUivals: []uint{12}, expectedFvals: []float64{10.1, 11.09}, }, { name: "set fvals beyond max", args: []string{"foo", "13", "10", "10.1", "11.09", "12.1"}, - expectedIvals: []int64{13}, - expectedUivals: []uint64{10}, + expectedIvals: []int{13}, + expectedUivals: []uint{10}, expectedFvals: []float64{10.1, 11.09}, errStr: "No help topic for '12.1'", }, @@ -66,8 +66,8 @@ func TestArgumentsRootCommand(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { cmd := buildMinimalTestCommand() - var ivals []int64 - var uivals []uint64 + var ivals []int + var uivals []uint var fvals []float64 cmd.Arguments = []Argument{ &IntArgs{ @@ -147,7 +147,7 @@ func TestArgumentsSubcommand(t *testing.T) { tests := []struct { name string args []string - expectedIval int64 + expectedIval int expectedSvals []string expectedTVals []time.Time errStr string @@ -176,7 +176,7 @@ func TestArgumentsSubcommand(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { cmd := buildMinimalTestCommand() - var ival int64 + var ival int var svals []string var tvals []time.Time cmd.Commands = []*Command{ diff --git a/godoc-current.txt b/godoc-current.txt index 250dadc12e..4a55270015 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -549,11 +549,55 @@ func (cmd *Command) HasName(name string) bool func (cmd *Command) Int(name string) int Int looks up the value of a local Int64Flag, returns 0 if not found -func (c *Command) IntArg(name string) int64 +func (cmd *Command) Int16(name string) int16 + Int16 looks up the value of a local Int16Flag, returns 0 if not found -func (c *Command) IntArgs(name string) []int64 +func (c *Command) Int16Arg(name string) int16 -func (cmd *Command) IntSlice(name string) []int64 +func (c *Command) Int16Args(name string) []int16 + +func (cmd *Command) Int16Slice(name string) []int16 + Int16Slice looks up the value of a local Int16SliceFlag, returns nil if not + found + +func (cmd *Command) Int32(name string) int32 + Int32 looks up the value of a local Int32Flag, returns 0 if not found + +func (c *Command) Int32Arg(name string) int32 + +func (c *Command) Int32Args(name string) []int32 + +func (cmd *Command) Int32Slice(name string) []int32 + Int32Slice looks up the value of a local Int32SliceFlag, returns nil if not + found + +func (cmd *Command) Int64(name string) int64 + Int64 looks up the value of a local Int64Flag, returns 0 if not found + +func (c *Command) Int64Arg(name string) int64 + +func (c *Command) Int64Args(name string) []int64 + +func (cmd *Command) Int64Slice(name string) []int64 + Int64Slice looks up the value of a local Int64SliceFlag, returns nil if not + found + +func (cmd *Command) Int8(name string) int8 + Int8 looks up the value of a local Int8Flag, returns 0 if not found + +func (c *Command) Int8Arg(name string) int8 + +func (c *Command) Int8Args(name string) []int8 + +func (cmd *Command) Int8Slice(name string) []int8 + Int8Slice looks up the value of a local Int8SliceFlag, returns nil if not + found + +func (c *Command) IntArg(name string) int + +func (c *Command) IntArgs(name string) []int + +func (cmd *Command) IntSlice(name string) []int IntSlice looks up the value of a local IntSliceFlag, returns nil if not found @@ -615,11 +659,55 @@ func (cmd *Command) ToFishCompletion() (string, error) func (cmd *Command) Uint(name string) uint Uint looks up the value of a local Uint64Flag, returns 0 if not found -func (c *Command) UintArg(name string) uint64 +func (cmd *Command) Uint16(name string) uint16 + Uint16 looks up the value of a local Uint16Flag, returns 0 if not found + +func (c *Command) Uint16Arg(name string) uint16 + +func (c *Command) Uint16Args(name string) []uint16 + +func (cmd *Command) Uint16Slice(name string) []uint16 + Uint16Slice looks up the value of a local Uint16SliceFlag, returns nil if + not found + +func (cmd *Command) Uint32(name string) uint32 + Uint32 looks up the value of a local Uint32Flag, returns 0 if not found + +func (c *Command) Uint32Arg(name string) uint32 + +func (c *Command) Uint32Args(name string) []uint32 + +func (cmd *Command) Uint32Slice(name string) []uint32 + Uint32Slice looks up the value of a local Uint32SliceFlag, returns nil if + not found + +func (cmd *Command) Uint64(name string) uint64 + Uint64 looks up the value of a local Uint64Flag, returns 0 if not found + +func (c *Command) Uint64Arg(name string) uint64 + +func (c *Command) Uint64Args(name string) []uint64 + +func (cmd *Command) Uint64Slice(name string) []uint64 + Uint64Slice looks up the value of a local Uint64SliceFlag, returns nil if + not found + +func (cmd *Command) Uint8(name string) uint8 + Uint8 looks up the value of a local Uint8Flag, returns 0 if not found + +func (c *Command) Uint8Arg(name string) uint8 -func (c *Command) UintArgs(name string) []uint64 +func (c *Command) Uint8Args(name string) []uint8 + +func (cmd *Command) Uint8Slice(name string) []uint8 + Uint8Slice looks up the value of a local Uint8SliceFlag, returns nil if not + found -func (cmd *Command) UintSlice(name string) []uint64 +func (c *Command) UintArg(name string) uint + +func (c *Command) UintArgs(name string) []uint + +func (cmd *Command) UintSlice(name string) []uint UintSlice looks up the value of a local UintSliceFlag, returns nil if not found @@ -947,14 +1035,20 @@ type FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice] type GenericFlag = FlagBase[Value, NoConfig, genericValue] -type IntArg = ArgumentBase[int64, IntegerConfig, intValue] +type Int16Arg = ArgumentBase[int16, IntegerConfig, intValue[int16]] + +type Int16Args = ArgumentsBase[int16, IntegerConfig, intValue[int16]] + +type Int16Flag = FlagBase[int16, IntegerConfig, intValue[int16]] -type IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] +type Int16Slice = SliceBase[int16, IntegerConfig, intValue[int16]] type Int16SliceFlag = FlagBase[[]int16, IntegerConfig, Int16Slice] type Int32Arg = ArgumentBase[int32, IntegerConfig, intValue[int32]] +type Int32Args = ArgumentsBase[int32, IntegerConfig, intValue[int32]] + type Int32Flag = FlagBase[int32, IntegerConfig, intValue[int32]] type Int32Slice = SliceBase[int32, IntegerConfig, intValue[int32]] @@ -963,6 +1057,8 @@ type Int32SliceFlag = FlagBase[[]int32, IntegerConfig, Int32Slice] type Int64Arg = ArgumentBase[int64, IntegerConfig, intValue[int64]] +type Int64Args = ArgumentsBase[int64, IntegerConfig, intValue[int64]] + type Int64Flag = FlagBase[int64, IntegerConfig, intValue[int64]] type Int64Slice = SliceBase[int64, IntegerConfig, intValue[int64]] @@ -971,6 +1067,8 @@ type Int64SliceFlag = FlagBase[[]int64, IntegerConfig, Int64Slice] type Int8Arg = ArgumentBase[int8, IntegerConfig, intValue[int8]] +type Int8Args = ArgumentsBase[int8, IntegerConfig, intValue[int8]] + type Int8Flag = FlagBase[int8, IntegerConfig, intValue[int8]] type Int8Slice = SliceBase[int8, IntegerConfig, intValue[int8]] @@ -979,6 +1077,8 @@ type Int8SliceFlag = FlagBase[[]int8, IntegerConfig, Int8Slice] type IntArg = ArgumentBase[int, IntegerConfig, intValue[int]] +type IntArgs = ArgumentsBase[int, IntegerConfig, intValue[int]] + type IntFlag = FlagBase[int, IntegerConfig, intValue[int]] type IntSlice = SliceBase[int, IntegerConfig, intValue[int]] @@ -1157,9 +1257,49 @@ type TimestampConfig struct { type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue] -type UintArg = ArgumentBase[uint64, IntegerConfig, uintValue] +type Uint16Arg = ArgumentBase[uint16, IntegerConfig, uintValue[uint16]] + +type Uint16Args = ArgumentsBase[uint16, IntegerConfig, uintValue[uint16]] + +type Uint16Flag = FlagBase[uint16, IntegerConfig, uintValue[uint16]] + +type Uint16Slice = SliceBase[uint16, IntegerConfig, uintValue[uint16]] + +type Uint16SliceFlag = FlagBase[[]uint16, IntegerConfig, Uint16Slice] + +type Uint32Arg = ArgumentBase[uint32, IntegerConfig, uintValue[uint32]] + +type Uint32Args = ArgumentsBase[uint32, IntegerConfig, uintValue[uint32]] + +type Uint32Flag = FlagBase[uint32, IntegerConfig, uintValue[uint32]] + +type Uint32Slice = SliceBase[uint32, IntegerConfig, uintValue[uint32]] + +type Uint32SliceFlag = FlagBase[[]uint32, IntegerConfig, Uint32Slice] + +type Uint64Arg = ArgumentBase[uint64, IntegerConfig, uintValue[uint64]] + +type Uint64Args = ArgumentsBase[uint64, IntegerConfig, uintValue[uint64]] + +type Uint64Flag = FlagBase[uint64, IntegerConfig, uintValue[uint64]] + +type Uint64Slice = SliceBase[uint64, IntegerConfig, uintValue[uint64]] + +type Uint64SliceFlag = FlagBase[[]uint64, IntegerConfig, Uint64Slice] + +type Uint8Arg = ArgumentBase[uint8, IntegerConfig, uintValue[uint8]] + +type Uint8Args = ArgumentsBase[uint8, IntegerConfig, uintValue[uint8]] + +type Uint8Flag = FlagBase[uint8, IntegerConfig, uintValue[uint8]] + +type Uint8Slice = SliceBase[uint8, IntegerConfig, uintValue[uint8]] + +type Uint8SliceFlag = FlagBase[[]uint8, IntegerConfig, Uint8Slice] + +type UintArg = ArgumentBase[uint, IntegerConfig, uintValue[uint]] -type UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] +type UintArgs = ArgumentsBase[uint, IntegerConfig, uintValue[uint]] type UintFlag = FlagBase[uint, IntegerConfig, uintValue[uint]] From 7d7f52cd970bcdfe5701bb9814196e25e56a080d Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 19 Apr 2025 16:34:50 -0600 Subject: [PATCH 13/18] run make v3approve --- testdata/godoc-v3.x.txt | 160 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 150 insertions(+), 10 deletions(-) diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index 250dadc12e..4a55270015 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -549,11 +549,55 @@ func (cmd *Command) HasName(name string) bool func (cmd *Command) Int(name string) int Int looks up the value of a local Int64Flag, returns 0 if not found -func (c *Command) IntArg(name string) int64 +func (cmd *Command) Int16(name string) int16 + Int16 looks up the value of a local Int16Flag, returns 0 if not found -func (c *Command) IntArgs(name string) []int64 +func (c *Command) Int16Arg(name string) int16 -func (cmd *Command) IntSlice(name string) []int64 +func (c *Command) Int16Args(name string) []int16 + +func (cmd *Command) Int16Slice(name string) []int16 + Int16Slice looks up the value of a local Int16SliceFlag, returns nil if not + found + +func (cmd *Command) Int32(name string) int32 + Int32 looks up the value of a local Int32Flag, returns 0 if not found + +func (c *Command) Int32Arg(name string) int32 + +func (c *Command) Int32Args(name string) []int32 + +func (cmd *Command) Int32Slice(name string) []int32 + Int32Slice looks up the value of a local Int32SliceFlag, returns nil if not + found + +func (cmd *Command) Int64(name string) int64 + Int64 looks up the value of a local Int64Flag, returns 0 if not found + +func (c *Command) Int64Arg(name string) int64 + +func (c *Command) Int64Args(name string) []int64 + +func (cmd *Command) Int64Slice(name string) []int64 + Int64Slice looks up the value of a local Int64SliceFlag, returns nil if not + found + +func (cmd *Command) Int8(name string) int8 + Int8 looks up the value of a local Int8Flag, returns 0 if not found + +func (c *Command) Int8Arg(name string) int8 + +func (c *Command) Int8Args(name string) []int8 + +func (cmd *Command) Int8Slice(name string) []int8 + Int8Slice looks up the value of a local Int8SliceFlag, returns nil if not + found + +func (c *Command) IntArg(name string) int + +func (c *Command) IntArgs(name string) []int + +func (cmd *Command) IntSlice(name string) []int IntSlice looks up the value of a local IntSliceFlag, returns nil if not found @@ -615,11 +659,55 @@ func (cmd *Command) ToFishCompletion() (string, error) func (cmd *Command) Uint(name string) uint Uint looks up the value of a local Uint64Flag, returns 0 if not found -func (c *Command) UintArg(name string) uint64 +func (cmd *Command) Uint16(name string) uint16 + Uint16 looks up the value of a local Uint16Flag, returns 0 if not found + +func (c *Command) Uint16Arg(name string) uint16 + +func (c *Command) Uint16Args(name string) []uint16 + +func (cmd *Command) Uint16Slice(name string) []uint16 + Uint16Slice looks up the value of a local Uint16SliceFlag, returns nil if + not found + +func (cmd *Command) Uint32(name string) uint32 + Uint32 looks up the value of a local Uint32Flag, returns 0 if not found + +func (c *Command) Uint32Arg(name string) uint32 + +func (c *Command) Uint32Args(name string) []uint32 + +func (cmd *Command) Uint32Slice(name string) []uint32 + Uint32Slice looks up the value of a local Uint32SliceFlag, returns nil if + not found + +func (cmd *Command) Uint64(name string) uint64 + Uint64 looks up the value of a local Uint64Flag, returns 0 if not found + +func (c *Command) Uint64Arg(name string) uint64 + +func (c *Command) Uint64Args(name string) []uint64 + +func (cmd *Command) Uint64Slice(name string) []uint64 + Uint64Slice looks up the value of a local Uint64SliceFlag, returns nil if + not found + +func (cmd *Command) Uint8(name string) uint8 + Uint8 looks up the value of a local Uint8Flag, returns 0 if not found + +func (c *Command) Uint8Arg(name string) uint8 -func (c *Command) UintArgs(name string) []uint64 +func (c *Command) Uint8Args(name string) []uint8 + +func (cmd *Command) Uint8Slice(name string) []uint8 + Uint8Slice looks up the value of a local Uint8SliceFlag, returns nil if not + found -func (cmd *Command) UintSlice(name string) []uint64 +func (c *Command) UintArg(name string) uint + +func (c *Command) UintArgs(name string) []uint + +func (cmd *Command) UintSlice(name string) []uint UintSlice looks up the value of a local UintSliceFlag, returns nil if not found @@ -947,14 +1035,20 @@ type FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice] type GenericFlag = FlagBase[Value, NoConfig, genericValue] -type IntArg = ArgumentBase[int64, IntegerConfig, intValue] +type Int16Arg = ArgumentBase[int16, IntegerConfig, intValue[int16]] + +type Int16Args = ArgumentsBase[int16, IntegerConfig, intValue[int16]] + +type Int16Flag = FlagBase[int16, IntegerConfig, intValue[int16]] -type IntArgs = ArgumentsBase[int64, IntegerConfig, intValue] +type Int16Slice = SliceBase[int16, IntegerConfig, intValue[int16]] type Int16SliceFlag = FlagBase[[]int16, IntegerConfig, Int16Slice] type Int32Arg = ArgumentBase[int32, IntegerConfig, intValue[int32]] +type Int32Args = ArgumentsBase[int32, IntegerConfig, intValue[int32]] + type Int32Flag = FlagBase[int32, IntegerConfig, intValue[int32]] type Int32Slice = SliceBase[int32, IntegerConfig, intValue[int32]] @@ -963,6 +1057,8 @@ type Int32SliceFlag = FlagBase[[]int32, IntegerConfig, Int32Slice] type Int64Arg = ArgumentBase[int64, IntegerConfig, intValue[int64]] +type Int64Args = ArgumentsBase[int64, IntegerConfig, intValue[int64]] + type Int64Flag = FlagBase[int64, IntegerConfig, intValue[int64]] type Int64Slice = SliceBase[int64, IntegerConfig, intValue[int64]] @@ -971,6 +1067,8 @@ type Int64SliceFlag = FlagBase[[]int64, IntegerConfig, Int64Slice] type Int8Arg = ArgumentBase[int8, IntegerConfig, intValue[int8]] +type Int8Args = ArgumentsBase[int8, IntegerConfig, intValue[int8]] + type Int8Flag = FlagBase[int8, IntegerConfig, intValue[int8]] type Int8Slice = SliceBase[int8, IntegerConfig, intValue[int8]] @@ -979,6 +1077,8 @@ type Int8SliceFlag = FlagBase[[]int8, IntegerConfig, Int8Slice] type IntArg = ArgumentBase[int, IntegerConfig, intValue[int]] +type IntArgs = ArgumentsBase[int, IntegerConfig, intValue[int]] + type IntFlag = FlagBase[int, IntegerConfig, intValue[int]] type IntSlice = SliceBase[int, IntegerConfig, intValue[int]] @@ -1157,9 +1257,49 @@ type TimestampConfig struct { type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue] -type UintArg = ArgumentBase[uint64, IntegerConfig, uintValue] +type Uint16Arg = ArgumentBase[uint16, IntegerConfig, uintValue[uint16]] + +type Uint16Args = ArgumentsBase[uint16, IntegerConfig, uintValue[uint16]] + +type Uint16Flag = FlagBase[uint16, IntegerConfig, uintValue[uint16]] + +type Uint16Slice = SliceBase[uint16, IntegerConfig, uintValue[uint16]] + +type Uint16SliceFlag = FlagBase[[]uint16, IntegerConfig, Uint16Slice] + +type Uint32Arg = ArgumentBase[uint32, IntegerConfig, uintValue[uint32]] + +type Uint32Args = ArgumentsBase[uint32, IntegerConfig, uintValue[uint32]] + +type Uint32Flag = FlagBase[uint32, IntegerConfig, uintValue[uint32]] + +type Uint32Slice = SliceBase[uint32, IntegerConfig, uintValue[uint32]] + +type Uint32SliceFlag = FlagBase[[]uint32, IntegerConfig, Uint32Slice] + +type Uint64Arg = ArgumentBase[uint64, IntegerConfig, uintValue[uint64]] + +type Uint64Args = ArgumentsBase[uint64, IntegerConfig, uintValue[uint64]] + +type Uint64Flag = FlagBase[uint64, IntegerConfig, uintValue[uint64]] + +type Uint64Slice = SliceBase[uint64, IntegerConfig, uintValue[uint64]] + +type Uint64SliceFlag = FlagBase[[]uint64, IntegerConfig, Uint64Slice] + +type Uint8Arg = ArgumentBase[uint8, IntegerConfig, uintValue[uint8]] + +type Uint8Args = ArgumentsBase[uint8, IntegerConfig, uintValue[uint8]] + +type Uint8Flag = FlagBase[uint8, IntegerConfig, uintValue[uint8]] + +type Uint8Slice = SliceBase[uint8, IntegerConfig, uintValue[uint8]] + +type Uint8SliceFlag = FlagBase[[]uint8, IntegerConfig, Uint8Slice] + +type UintArg = ArgumentBase[uint, IntegerConfig, uintValue[uint]] -type UintArgs = ArgumentsBase[uint64, IntegerConfig, uintValue] +type UintArgs = ArgumentsBase[uint, IntegerConfig, uintValue[uint]] type UintFlag = FlagBase[uint, IntegerConfig, uintValue[uint]] From cfabbb61473300b0ce975c459ec55ae06c788ead Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 19 Apr 2025 16:52:35 -0600 Subject: [PATCH 14/18] Add tests --- args_test.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/args_test.go b/args_test.go index 944382fe46..aa4ef1bdad 100644 --- a/args_test.go +++ b/args_test.go @@ -8,6 +8,112 @@ import ( "github.com/stretchr/testify/require" ) +type intnum interface { + int | int8 | int16 | int32 | int64 +} + +type uintnum interface { + uint | uint8 | uint16 | uint32 | uint64 +} + +func intTypes[T intnum](t *testing.T) { + cmd := buildMinimalTestCommand() + var ival T + cmd.Arguments = []Argument{ + &ArgumentBase[T, IntegerConfig, intValue[T]]{ + Name: "ia", + Destination: &ival, + }, + } + + err := cmd.Run(buildTestContext(t), []string{"foo", "10"}) + r := require.New(t) + r.NoError(err) + r.Equal(T(10), ival) + r.Equal(T(10), arg[T]("ia", cmd)) +} + +func intArrTypes[T intnum](t *testing.T) { + cmd := buildMinimalTestCommand() + var ival []T + cmd.Arguments = []Argument{ + &ArgumentsBase[T, IntegerConfig, intValue[T]]{ + Name: "ia", + Min: 1, + Max: -1, + Destination: &ival, + }, + } + + err := cmd.Run(buildTestContext(t), []string{"foo", "10", "11", "12"}) + r := require.New(t) + r.NoError(err) + r.Equal([]T{10, 11, 12}, ival) + r.Equal([]T{10, 11, 12}, arg[[]T]("ia", cmd)) +} + +func uintTypes[T uintnum](t *testing.T) { + cmd := buildMinimalTestCommand() + var ival T + cmd.Arguments = []Argument{ + &ArgumentBase[T, IntegerConfig, uintValue[T]]{ + Name: "ia", + Destination: &ival, + }, + } + + err := cmd.Run(buildTestContext(t), []string{"foo", "10"}) + r := require.New(t) + r.NoError(err) + r.Equal(T(10), ival) + r.Equal(T(10), arg[T]("ia", cmd)) +} + +func uintArrTypes[T uintnum](t *testing.T) { + cmd := buildMinimalTestCommand() + var ival []T + cmd.Arguments = []Argument{ + &ArgumentsBase[T, IntegerConfig, uintValue[T]]{ + Name: "ia", + Min: 1, + Max: -1, + Destination: &ival, + }, + } + + err := cmd.Run(buildTestContext(t), []string{"foo", "10", "11", "12"}) + r := require.New(t) + r.NoError(err) + r.Equal([]T{10, 11, 12}, ival) + r.Equal([]T{10, 11, 12}, arg[[]T]("ia", cmd)) +} + +func TestArgumentsRootCommandAllTypes(t *testing.T) { + intTypes[int](t) + intTypes[int8](t) + intTypes[int16](t) + intTypes[int32](t) + intTypes[int64](t) + + uintTypes[uint](t) + uintTypes[uint8](t) + uintTypes[uint16](t) + uintTypes[uint32](t) + uintTypes[uint64](t) + + intArrTypes[int](t) + intArrTypes[int8](t) + intArrTypes[int16](t) + intArrTypes[int32](t) + intArrTypes[int64](t) + + uintArrTypes[uint](t) + uintArrTypes[uint8](t) + uintArrTypes[uint16](t) + uintArrTypes[uint32](t) + uintArrTypes[uint64](t) +} + func TestArgumentsRootCommand(t *testing.T) { tests := []struct { name string From 7e7136378ad1c9c71dacf0570d06f50d843c83de Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 19 Apr 2025 16:57:50 -0600 Subject: [PATCH 15/18] Add more tests --- args_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/args_test.go b/args_test.go index aa4ef1bdad..7089317a6f 100644 --- a/args_test.go +++ b/args_test.go @@ -206,8 +206,16 @@ func TestArgumentsRootCommand(t *testing.T) { r.Equal(test.expectedIvals, ivals) } r.Equal(test.expectedIvals, cmd.IntArgs("ia")) + r.Equal(int8(0), cmd.Int8Arg("ia")) + r.Equal(int16(0), cmd.Int16Arg("ia")) + r.Equal(int32(0), cmd.Int32Arg("ia")) + r.Equal(int64(0), cmd.Int64Arg("ia")) r.Equal(test.expectedFvals, cmd.FloatArgs("fa")) r.Equal(test.expectedUivals, cmd.UintArgs("uia")) + r.Equal(uint8(0), cmd.Uint8Arg("uia")) + r.Equal(uint16(0), cmd.Uint16Arg("uia")) + r.Equal(uint32(0), cmd.Uint32Arg("uia")) + r.Equal(uint64(0), cmd.Uint64Arg("uia")) /*if test.expectedFvals != nil { r.Equal(test.expectedFvals, fvals) }*/ From 941ae99a38fd80c6f387e25ec124cd7d31403058 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 19 Apr 2025 17:12:29 -0600 Subject: [PATCH 16/18] Add tests --- args_test.go | 113 +++++++++++++++++++++------------------------------ 1 file changed, 47 insertions(+), 66 deletions(-) diff --git a/args_test.go b/args_test.go index 7089317a6f..0670c48b2d 100644 --- a/args_test.go +++ b/args_test.go @@ -8,19 +8,11 @@ import ( "github.com/stretchr/testify/require" ) -type intnum interface { - int | int8 | int16 | int32 | int64 -} - -type uintnum interface { - uint | uint8 | uint16 | uint32 | uint64 -} - -func intTypes[T intnum](t *testing.T) { +func TestArgsIntTypes(t *testing.T) { cmd := buildMinimalTestCommand() - var ival T + var ival int cmd.Arguments = []Argument{ - &ArgumentBase[T, IntegerConfig, intValue[T]]{ + &IntArg{ Name: "ia", Destination: &ival, }, @@ -29,15 +21,19 @@ func intTypes[T intnum](t *testing.T) { err := cmd.Run(buildTestContext(t), []string{"foo", "10"}) r := require.New(t) r.NoError(err) - r.Equal(T(10), ival) - r.Equal(T(10), arg[T]("ia", cmd)) + r.Equal(10, ival) + r.Equal(10, cmd.IntArg("ia")) + r.Equal(int8(0), cmd.Int8Arg("ia")) + r.Equal(int16(0), cmd.Int16Arg("ia")) + r.Equal(int32(0), cmd.Int32Arg("ia")) + r.Equal(int64(0), cmd.Int64Arg("ia")) } -func intArrTypes[T intnum](t *testing.T) { +func TestArgsIntSliceTypes(t *testing.T) { cmd := buildMinimalTestCommand() - var ival []T + var ival []int cmd.Arguments = []Argument{ - &ArgumentsBase[T, IntegerConfig, intValue[T]]{ + &IntArgs{ Name: "ia", Min: 1, Max: -1, @@ -45,18 +41,22 @@ func intArrTypes[T intnum](t *testing.T) { }, } - err := cmd.Run(buildTestContext(t), []string{"foo", "10", "11", "12"}) + err := cmd.Run(buildTestContext(t), []string{"foo", "10", "20", "30"}) r := require.New(t) r.NoError(err) - r.Equal([]T{10, 11, 12}, ival) - r.Equal([]T{10, 11, 12}, arg[[]T]("ia", cmd)) + r.Equal([]int{10, 20, 30}, ival) + r.Equal([]int{10, 20, 30}, cmd.IntArgs("ia")) + r.Nil(cmd.Int8Args("ia")) + r.Nil(cmd.Int16Args("ia")) + r.Nil(cmd.Int32Args("ia")) + r.Nil(cmd.Int64Args("ia")) } -func uintTypes[T uintnum](t *testing.T) { +func TestArgsUintTypes(t *testing.T) { cmd := buildMinimalTestCommand() - var ival T + var ival uint cmd.Arguments = []Argument{ - &ArgumentBase[T, IntegerConfig, uintValue[T]]{ + &UintArg{ Name: "ia", Destination: &ival, }, @@ -65,15 +65,19 @@ func uintTypes[T uintnum](t *testing.T) { err := cmd.Run(buildTestContext(t), []string{"foo", "10"}) r := require.New(t) r.NoError(err) - r.Equal(T(10), ival) - r.Equal(T(10), arg[T]("ia", cmd)) + r.Equal(uint(10), ival) + r.Equal(uint(10), cmd.UintArg("ia")) + r.Equal(uint8(0), cmd.Uint8Arg("ia")) + r.Equal(uint16(0), cmd.Uint16Arg("ia")) + r.Equal(uint32(0), cmd.Uint32Arg("ia")) + r.Equal(uint64(0), cmd.Uint64Arg("ia")) } -func uintArrTypes[T uintnum](t *testing.T) { +func TestArgsUintSliceTypes(t *testing.T) { cmd := buildMinimalTestCommand() - var ival []T + var ival []uint cmd.Arguments = []Argument{ - &ArgumentsBase[T, IntegerConfig, uintValue[T]]{ + &UintArgs{ Name: "ia", Min: 1, Max: -1, @@ -81,37 +85,15 @@ func uintArrTypes[T uintnum](t *testing.T) { }, } - err := cmd.Run(buildTestContext(t), []string{"foo", "10", "11", "12"}) + err := cmd.Run(buildTestContext(t), []string{"foo", "10", "20", "30"}) r := require.New(t) r.NoError(err) - r.Equal([]T{10, 11, 12}, ival) - r.Equal([]T{10, 11, 12}, arg[[]T]("ia", cmd)) -} - -func TestArgumentsRootCommandAllTypes(t *testing.T) { - intTypes[int](t) - intTypes[int8](t) - intTypes[int16](t) - intTypes[int32](t) - intTypes[int64](t) - - uintTypes[uint](t) - uintTypes[uint8](t) - uintTypes[uint16](t) - uintTypes[uint32](t) - uintTypes[uint64](t) - - intArrTypes[int](t) - intArrTypes[int8](t) - intArrTypes[int16](t) - intArrTypes[int32](t) - intArrTypes[int64](t) - - uintArrTypes[uint](t) - uintArrTypes[uint8](t) - uintArrTypes[uint16](t) - uintArrTypes[uint32](t) - uintArrTypes[uint64](t) + r.Equal([]uint{10, 20, 30}, ival) + r.Equal([]uint{10, 20, 30}, cmd.UintArgs("ia")) + r.Nil(cmd.Uint8Args("ia")) + r.Nil(cmd.Uint16Args("ia")) + r.Nil(cmd.Uint32Args("ia")) + r.Nil(cmd.Uint64Args("ia")) } func TestArgumentsRootCommand(t *testing.T) { @@ -206,16 +188,8 @@ func TestArgumentsRootCommand(t *testing.T) { r.Equal(test.expectedIvals, ivals) } r.Equal(test.expectedIvals, cmd.IntArgs("ia")) - r.Equal(int8(0), cmd.Int8Arg("ia")) - r.Equal(int16(0), cmd.Int16Arg("ia")) - r.Equal(int32(0), cmd.Int32Arg("ia")) - r.Equal(int64(0), cmd.Int64Arg("ia")) r.Equal(test.expectedFvals, cmd.FloatArgs("fa")) r.Equal(test.expectedUivals, cmd.UintArgs("uia")) - r.Equal(uint8(0), cmd.Uint8Arg("uia")) - r.Equal(uint16(0), cmd.Uint16Arg("uia")) - r.Equal(uint32(0), cmd.Uint32Arg("uia")) - r.Equal(uint64(0), cmd.Uint64Arg("uia")) /*if test.expectedFvals != nil { r.Equal(test.expectedFvals, fvals) }*/ @@ -252,9 +226,16 @@ func TestArgumentsInvalidType(t *testing.T) { r := require.New(t) r.Nil(cmd.StringArgs("ia")) r.Nil(cmd.FloatArgs("ia")) - r.Nil(cmd.UintArgs("ia")) + r.Nil(cmd.Int8Args("ia")) + r.Nil(cmd.Int16Args("ia")) + r.Nil(cmd.Int32Args("ia")) + r.Nil(cmd.Int64Args("ia")) r.Nil(cmd.TimestampArgs("ia")) - r.Nil(cmd.IntArgs("uia")) + r.Nil(cmd.UintArgs("ia")) + r.Nil(cmd.Uint8Args("ia")) + r.Nil(cmd.Uint16Args("ia")) + r.Nil(cmd.Uint32Args("ia")) + r.Nil(cmd.Uint64Args("ia")) } func TestArgumentsSubcommand(t *testing.T) { From 2bd3c5f1974298e0cc7fc056a4eb0bb14331cee4 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 19 Apr 2025 17:43:44 -0600 Subject: [PATCH 17/18] Add tests --- args_test.go | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/args_test.go b/args_test.go index 0670c48b2d..f5b5bcf6b1 100644 --- a/args_test.go +++ b/args_test.go @@ -23,10 +23,15 @@ func TestArgsIntTypes(t *testing.T) { r.NoError(err) r.Equal(10, ival) r.Equal(10, cmd.IntArg("ia")) + r.Equal(0, cmd.IntArg("iab")) r.Equal(int8(0), cmd.Int8Arg("ia")) r.Equal(int16(0), cmd.Int16Arg("ia")) r.Equal(int32(0), cmd.Int32Arg("ia")) r.Equal(int64(0), cmd.Int64Arg("ia")) + r.Equal(float64(0), cmd.FloatArg("ia")) + r.Empty(cmd.StringArg("ia")) + + r.Error(cmd.Run(buildTestContext(t), []string{"foo", "10.0"})) } func TestArgsIntSliceTypes(t *testing.T) { @@ -50,6 +55,8 @@ func TestArgsIntSliceTypes(t *testing.T) { r.Nil(cmd.Int16Args("ia")) r.Nil(cmd.Int32Args("ia")) r.Nil(cmd.Int64Args("ia")) + + r.Error(cmd.Run(buildTestContext(t), []string{"foo", "10", "20.0"})) } func TestArgsUintTypes(t *testing.T) { @@ -67,10 +74,13 @@ func TestArgsUintTypes(t *testing.T) { r.NoError(err) r.Equal(uint(10), ival) r.Equal(uint(10), cmd.UintArg("ia")) + r.Equal(uint(0), cmd.UintArg("iab")) r.Equal(uint8(0), cmd.Uint8Arg("ia")) r.Equal(uint16(0), cmd.Uint16Arg("ia")) r.Equal(uint32(0), cmd.Uint32Arg("ia")) r.Equal(uint64(0), cmd.Uint64Arg("ia")) + + r.Error(cmd.Run(buildTestContext(t), []string{"foo", "10.0"})) } func TestArgsUintSliceTypes(t *testing.T) { @@ -94,6 +104,8 @@ func TestArgsUintSliceTypes(t *testing.T) { r.Nil(cmd.Uint16Args("ia")) r.Nil(cmd.Uint32Args("ia")) r.Nil(cmd.Uint64Args("ia")) + + r.Error(cmd.Run(buildTestContext(t), []string{"foo", "10", "20.0"})) } func TestArgumentsRootCommand(t *testing.T) { @@ -195,23 +207,6 @@ func TestArgumentsRootCommand(t *testing.T) { }*/ }) } - - /* - cmd.Arguments = append(cmd.Arguments, - - &StringArgs{ - Name: "sa", - }, - &UintArgs{ - Name: "ua", - Min: 2, - Max: 1, // max is less than min - }, - - ) - - require.NoError(t, cmd.Run(context.Background(), []string{"foo", "10"})) - */ } func TestArgumentsInvalidType(t *testing.T) { @@ -230,6 +225,7 @@ func TestArgumentsInvalidType(t *testing.T) { r.Nil(cmd.Int16Args("ia")) r.Nil(cmd.Int32Args("ia")) r.Nil(cmd.Int64Args("ia")) + r.Equal(time.Time{}, cmd.TimestampArg("ia")) r.Nil(cmd.TimestampArgs("ia")) r.Nil(cmd.UintArgs("ia")) r.Nil(cmd.Uint8Args("ia")) From f87d093a10e59c00a33ae711bd4b926a33c8b942 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 19 Apr 2025 17:46:37 -0600 Subject: [PATCH 18/18] Add tests --- args_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/args_test.go b/args_test.go index f5b5bcf6b1..90f6baf2c6 100644 --- a/args_test.go +++ b/args_test.go @@ -328,6 +328,33 @@ func TestArgumentsSubcommand(t *testing.T) { } } +func TestArgUsage(t *testing.T) { + arg := &IntArg{ + Name: "ia", + } + tests := []struct { + name string + usage string + expected string + }{ + { + name: "default", + expected: "ia", + }, + { + name: "usage", + usage: "foo-usage", + expected: "foo-usage", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + arg.UsageText = test.usage + require.Equal(t, test.expected, arg.Usage()) + }) + } +} + func TestArgsUsage(t *testing.T) { arg := &IntArgs{ Name: "ia",