diff --git a/flag_string_slice.go b/flag_string_slice.go index b24664b..ac363bf 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -71,6 +71,7 @@ type StringSliceFlag struct { Value *StringSlice DefaultText string HasBeenSet bool + Destination *StringSlice } // IsSet returns whether or not the flag has been set through env or file @@ -117,13 +118,20 @@ func (f *StringSliceFlag) GetValue() string { func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { f.Value = &StringSlice{} + destination := f.Value + if f.Destination != nil { + destination = f.Destination + } for _, s := range strings.Split(val, ",") { - if err := f.Value.Set(strings.TrimSpace(s)); err != nil { + 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) } } + // Set this to false so that we reset the slice if we then set values from + // flags that have already been set by the environment. + destination.hasBeenSet = false f.HasBeenSet = true } @@ -131,6 +139,12 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { if f.Value == nil { f.Value = &StringSlice{} } + + if f.Destination != nil { + set.Var(f.Destination, name, f.Usage) + continue + } + set.Var(f.Value, name, f.Usage) } diff --git a/flag_test.go b/flag_test.go index f0c444e..02f5d7d 100644 --- a/flag_test.go +++ b/flag_test.go @@ -59,7 +59,7 @@ func TestFlagsFromEnv(t *testing.T) { newSetStringSlice := func(defaults ...string) StringSlice { s := NewStringSlice(defaults...) - s.hasBeenSet = true + s.hasBeenSet = false return *s } @@ -917,6 +917,47 @@ func TestParseMultiStringSliceWithDefaults(t *testing.T) { }).Run([]string{"run", "-s", "10", "-s", "20"}) } +func TestParseMultiStringSliceWithDestination(t *testing.T) { + dest := &StringSlice{} + _ = (&App{ + Flags: []Flag{ + &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest}, + }, + Action: func(ctx *Context) error { + expected := []string{"10", "20"} + if !reflect.DeepEqual(dest.slice, expected) { + t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve")) + } + if !reflect.DeepEqual(dest.slice, expected) { + t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s")) + } + return nil + }, + }).Run([]string{"run", "-s", "10", "-s", "20"}) +} + +func TestParseMultiStringSliceWithDestinationAndEnv(t *testing.T) { + os.Clearenv() + _ = os.Setenv("APP_INTERVALS", "20,30,40") + + dest := &StringSlice{} + _ = (&App{ + Flags: []Flag{ + &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}}, + }, + Action: func(ctx *Context) error { + expected := []string{"10", "20"} + if !reflect.DeepEqual(dest.slice, expected) { + t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve")) + } + if !reflect.DeepEqual(dest.slice, expected) { + t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s")) + } + return nil + }, + }).Run([]string{"run", "-s", "10", "-s", "20"}) +} + func TestParseMultiStringSliceWithDefaultsUnset(t *testing.T) { _ = (&App{ Flags: []Flag{ @@ -1014,6 +1055,27 @@ func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) { }).Run([]string{"run"}) } +func TestParseMultiStringSliceFromEnvWithDestination(t *testing.T) { + os.Clearenv() + _ = os.Setenv("APP_INTERVALS", "20,30,40") + + dest := &StringSlice{} + _ = (&App{ + Flags: []Flag{ + &StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}}, + }, + Action: func(ctx *Context) error { + if !reflect.DeepEqual(dest.slice, []string{"20", "30", "40"}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(dest.slice, []string{"20", "30", "40"}) { + t.Errorf("short name not set from env") + } + return nil + }, + }).Run([]string{"run"}) +} + func TestParseMultiInt(t *testing.T) { _ = (&App{ Flags: []Flag{