diff --git a/app_test.go b/app_test.go index 0829ac6193..28b497c887 100644 --- a/app_test.go +++ b/app_test.go @@ -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: +} + func TestApp_Run(t *testing.T) { s := "" diff --git a/flag.go b/flag.go index b85bafd546..d2a33f01f9 100644 --- a/flag.go +++ b/flag.go @@ -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, ",") +} diff --git a/flag_float64_slice.go b/flag_float64_slice.go index d573ae5f35..219c50150c 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -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 } @@ -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) } diff --git a/flag_int64_slice.go b/flag_int64_slice.go index 16f5665b0a..6ea7ae8b90 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -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 } @@ -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) } diff --git a/flag_int_slice.go b/flag_int_slice.go index b0032299af..38f6a33b06 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -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 } @@ -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) } diff --git a/flag_string_slice.go b/flag_string_slice.go index 3879235779..ff0a4b7da6 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -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 } @@ -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) } diff --git a/flag_test.go b/flag_test.go index 843f646a9d..e385276cd7 100644 --- a/flag_test.go +++ b/flag_test.go @@ -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")