Skip to content

Commit

Permalink
Merge pull request #1377 from urfave/vipally-ally_fix_multi_val
Browse files Browse the repository at this point in the history
Accept multi-value input on slice flags (#1241)
  • Loading branch information
meatballhat committed Apr 26, 2022
2 parents a07f0d1 + 6538e95 commit 76418f2
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 20 deletions.
34 changes: 34 additions & 0 deletions app_test.go
Expand Up @@ -390,6 +390,40 @@ func ExampleApp_Run_zshComplete() {
// h:Shows a list of commands or help for one command
}

func ExampleApp_Run_sliceValues() {
// set args for examples sake
os.Args = []string{"multi_values",
"--stringSclice", "parsed1,parsed2", "--stringSclice", "parsed3,parsed4",
"--float64Sclice", "13.3,14.4", "--float64Sclice", "15.5,16.6",
"--int64Sclice", "13,14", "--int64Sclice", "15,16",
"--intSclice", "13,14", "--intSclice", "15,16",
}
app := NewApp()
app.Name = "multi_values"
app.Flags = []Flag{
&StringSliceFlag{Name: "stringSclice"},
&Float64SliceFlag{Name: "float64Sclice"},
&Int64SliceFlag{Name: "int64Sclice"},
&IntSliceFlag{Name: "intSclice"},
}
app.Action = func(ctx *Context) error {
for i, v := range ctx.FlagNames() {
fmt.Printf("%d-%s %#v\n", i, v, ctx.Value(v))
}
err := ctx.Err()
fmt.Println("error:", err)
return err
}

_ = app.Run(os.Args)
// Output:
// 0-float64Sclice cli.Float64Slice{slice:[]float64{13.3, 14.4, 15.5, 16.6}, hasBeenSet:true}
// 1-int64Sclice cli.Int64Slice{slice:[]int64{13, 14, 15, 16}, hasBeenSet:true}
// 2-intSclice cli.IntSlice{slice:[]int{13, 14, 15, 16}, hasBeenSet:true}
// 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, hasBeenSet:true}
// error: <nil>
}

func TestApp_Run(t *testing.T) {
s := ""

Expand Down
4 changes: 4 additions & 0 deletions flag.go
Expand Up @@ -402,3 +402,7 @@ func flagFromEnvOrFile(envVars []string, filePath string) (val string, ok bool)
}
return "", false
}

func flagSplitMultiValues(val string) []string {
return strings.Split(val, ",")
}
14 changes: 8 additions & 6 deletions flag_float64_slice.go
Expand Up @@ -43,12 +43,14 @@ func (f *Float64Slice) Set(value string) error {
return nil
}

tmp, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
for _, s := range flagSplitMultiValues(value) {
tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
if err != nil {
return err
}

f.slice = append(f.slice, tmp)
f.slice = append(f.slice, tmp)
}
return nil
}

Expand Down Expand Up @@ -151,7 +153,7 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
if val != "" {
f.Value = &Float64Slice{}

for _, s := range strings.Split(val, ",") {
for _, s := range flagSplitMultiValues(val) {
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as float64 slice value for flag %s: %s", f.Value, f.Name, err)
}
Expand Down
14 changes: 8 additions & 6 deletions flag_int64_slice.go
Expand Up @@ -43,12 +43,14 @@ func (i *Int64Slice) Set(value string) error {
return nil
}

tmp, err := strconv.ParseInt(value, 0, 64)
if err != nil {
return err
}
for _, s := range flagSplitMultiValues(value) {
tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64)
if err != nil {
return err
}

i.slice = append(i.slice, tmp)
i.slice = append(i.slice, tmp)
}

return nil
}
Expand Down Expand Up @@ -151,7 +153,7 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
f.Value = &Int64Slice{}

for _, s := range strings.Split(val, ",") {
for _, s := range flagSplitMultiValues(val) {
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int64 slice value for flag %s: %s", val, f.Name, err)
}
Expand Down
14 changes: 8 additions & 6 deletions flag_int_slice.go
Expand Up @@ -54,12 +54,14 @@ func (i *IntSlice) Set(value string) error {
return nil
}

tmp, err := strconv.ParseInt(value, 0, 64)
if err != nil {
return err
}
for _, s := range flagSplitMultiValues(value) {
tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64)
if err != nil {
return err
}

i.slice = append(i.slice, int(tmp))
i.slice = append(i.slice, int(tmp))
}

return nil
}
Expand Down Expand Up @@ -162,7 +164,7 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
f.Value = &IntSlice{}

for _, s := range strings.Split(val, ",") {
for _, s := range flagSplitMultiValues(val) {
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int slice value for flag %s: %s", val, f.Name, err)
}
Expand Down
6 changes: 4 additions & 2 deletions flag_string_slice.go
Expand Up @@ -42,7 +42,9 @@ func (s *StringSlice) Set(value string) error {
return nil
}

s.slice = append(s.slice, value)
for _, t := range flagSplitMultiValues(value) {
s.slice = append(s.slice, strings.TrimSpace(t))
}

return nil
}
Expand Down Expand Up @@ -160,7 +162,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
destination = f.Destination
}

for _, s := range strings.Split(val, ",") {
for _, s := range flagSplitMultiValues(val) {
if err := destination.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as string value for flag %s: %s", val, f.Name, err)
}
Expand Down
48 changes: 48 additions & 0 deletions flag_test.go
Expand Up @@ -2230,6 +2230,54 @@ func TestFlagDefaultValue(t *testing.T) {
}
}

type flagValueTestCase struct {
name string
flag Flag
toParse []string
expect string
}

func TestFlagValue(t *testing.T) {
cases := []*flagValueTestCase{
&flagValueTestCase{
name: "stringSclice",
flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")},
toParse: []string{"--flag", "parsed,parsed2", "--flag", "parsed3,parsed4"},
expect: `[parsed parsed2 parsed3 parsed4]`,
},
&flagValueTestCase{
name: "float64Sclice",
flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)},
toParse: []string{"--flag", "13.3,14.4", "--flag", "15.5,16.6"},
expect: `[]float64{13.3, 14.4, 15.5, 16.6}`,
},
&flagValueTestCase{
name: "int64Sclice",
flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)},
toParse: []string{"--flag", "13,14", "--flag", "15,16"},
expect: `[]int64{13, 14, 15, 16}`,
},
&flagValueTestCase{
name: "intSclice",
flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)},
toParse: []string{"--flag", "13,14", "--flag", "15,16"},
expect: `[]int{13, 14, 15, 16}`,
},
}
for i, v := range cases {
set := flag.NewFlagSet("test", 0)
set.SetOutput(ioutil.Discard)
_ = v.flag.Apply(set)
if err := set.Parse(v.toParse); err != nil {
t.Error(err)
}
f := set.Lookup("flag")
if got := f.Value.String(); got != v.expect {
t.Errorf("TestFlagValue %d-%s\nexpect:%s\ngot:%s", i, v.name, v.expect, got)
}
}
}

func TestTimestampFlagApply_WithDestination(t *testing.T) {
var destination Timestamp
expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
Expand Down

0 comments on commit 76418f2

Please sign in to comment.