Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept multi-value input on slice flags (#1241) #1377

Merged
merged 3 commits into from Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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