diff --git a/app_test.go b/app_test.go index b05e8e62d5..6f2115cb72 100644 --- a/app_test.go +++ b/app_test.go @@ -428,7 +428,7 @@ func ExampleApp_Run_sliceValues() { // 0-float64Sclice cli.Float64Slice{slice:[]float64{13.3, 14.4, 15.5, 16.6}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} // 1-int64Sclice cli.Int64Slice{slice:[]int64{13, 14, 15, 16}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} // 2-intSclice cli.IntSlice{slice:[]int{13, 14, 15, 16}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} - // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} + // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true, noTrimSpace:false} // error: } diff --git a/flag-spec.yaml b/flag-spec.yaml index ed4db985d0..92168245f7 100644 --- a/flag-spec.yaml +++ b/flag-spec.yaml @@ -101,6 +101,8 @@ flag_types: type: bool - name: Action type: "func(*Context, []string) error" + - name: NoTrimSpace + type: bool time.Duration: struct_fields: - name: Action diff --git a/flag_string_slice.go b/flag_string_slice.go index 82410dbc8b..b19d9715d1 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -10,9 +10,10 @@ import ( // StringSlice wraps a []string to satisfy flag.Value type StringSlice struct { - slice []string - separator separatorSpec - hasBeenSet bool + slice []string + separator separatorSpec + hasBeenSet bool + noTrimSpace bool } // NewStringSlice creates a *StringSlice with default values @@ -45,6 +46,9 @@ func (s *StringSlice) Set(value string) error { } for _, t := range s.separator.flagSplitMultiValues(value) { + if !s.noTrimSpace { + t = strings.TrimSpace(t) + } s.slice = append(s.slice, t) } @@ -149,6 +153,8 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { setValue.WithSeparatorSpec(f.separator) } + setValue.noTrimSpace = f.NoTrimSpace + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { diff --git a/flag_test.go b/flag_test.go index 132a9ede4a..e08a5e2ef9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -774,7 +774,7 @@ func TestStringSliceFlag_MatchStringFlagBehavior(t *testing.T) { app := App{ Flags: []Flag{ &StringFlag{Name: "string"}, - &StringSliceFlag{Name: "slice"}, + &StringSliceFlag{Name: "slice", NoTrimSpace: true}, }, Action: func(ctx *Context) error { f1, f2 := ctx.String("string"), ctx.StringSlice("slice") @@ -797,6 +797,52 @@ func TestStringSliceFlag_MatchStringFlagBehavior(t *testing.T) { } } +func TestStringSliceFlag_TrimSpace(t *testing.T) { + t.Parallel() + + tests := []struct { + in, out string + }{ + {" asd", "asd"}, + {"123 ", "123"}, + {" asd ", "asd"}, + } + for testNum, tt := range tests { + tt := tt + t.Run(fmt.Sprintf("%d", testNum), func(t *testing.T) { + t.Parallel() + + app := App{ + Flags: []Flag{ + &StringSliceFlag{Name: "trim"}, + &StringSliceFlag{Name: "no-trim", NoTrimSpace: true}, + }, + Action: func(ctx *Context) error { + flagTrim, flagNoTrim := ctx.StringSlice("trim"), ctx.StringSlice("no-trim") + if l := len(flagTrim); l != 1 { + t.Fatalf("slice flag 'trim' should result in exactly one value, got %d", l) + } + if l := len(flagNoTrim); l != 1 { + t.Fatalf("slice flag 'no-trim' should result in exactly one value, got %d", l) + } + + if v := flagTrim[0]; v != tt.out { + t.Errorf("Expected trimmed value %q, got %q", tt.out, v) + } + if v := flagNoTrim[0]; v != tt.in { + t.Errorf("Expected no trimmed value%q, got %q", tt.out, v) + } + return nil + }, + } + + if err := app.Run([]string{"", "--trim", tt.in, "--no-trim", tt.in}); err != nil { + t.Errorf("app run error: %s", err) + } + }) + } +} + var intFlagTests = []struct { name string expected string diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 8c29f6ee03..39813cbe58 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -270,6 +270,8 @@ type StringSliceFlag struct { TakesFile bool Action func(*Context, []string) error + + NoTrimSpace bool } // IsSet returns whether or not the flag has been set through env or file