diff --git a/altsrc/flag.go b/altsrc/flag.go index 8697b29d0a..d8256d7605 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -148,6 +148,37 @@ func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC return nil } +// ApplyInputSourceValue applies a Int64Slice value if required +func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.Int64SliceFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.Int64Slice(name) + if err != nil { + return err + } + if value == nil { + continue + } + var sliceValue = *(cli.NewInt64Slice(value...)) + for _, n := range f.Names() { + underlyingFlag := f.set.Lookup(n) + if underlyingFlag == nil { + continue + } + underlyingFlag.Value = &sliceValue + } + if f.Destination != nil { + f.Destination.Set(sliceValue.Serialize()) + } + } + return nil +} + // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index caf62d87f2..ad66dab43c 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -257,6 +257,29 @@ func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { refute(t, c.IntSlice("test"), []int{3, 4}) } +func TestInt64SliceFlagApplyInputSourceValue(t *testing.T) { + dest := cli.NewInt64Slice() + tis := testApplyInputSource{ + Flag: NewInt64SliceFlag(&cli.Int64SliceFlag{Name: "test", Destination: dest}), + FlagName: "test", + MapValue: []interface{}{int64(1), int64(2)}, + } + c := runTest(t, tis) + expect(t, c.Int64Slice("test"), []int64{1, 2}) + expect(t, dest.Value(), []int64{1, 2}) + + // reset dest + dest = cli.NewInt64Slice() + tis = testApplyInputSource{ + Flag: NewInt64SliceFlag(&cli.Int64SliceFlag{Name: "test", Destination: dest}), + FlagName: "test", + MapValue: []interface{}{int64(1), int64(2)}, + } + c = runRacyTest(t, tis) + refute(t, c.IntSlice("test"), []int64{1, 2}) + refute(t, dest.Value(), []int64{1, 2}) +} + func TestBoolApplyInputSourceMethodSet(t *testing.T) { tis := testApplyInputSource{ Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), diff --git a/altsrc/input_source_context.go b/altsrc/input_source_context.go index d743253050..24579d16a9 100644 --- a/altsrc/input_source_context.go +++ b/altsrc/input_source_context.go @@ -20,6 +20,7 @@ type InputSourceContext interface { String(name string) (string, error) StringSlice(name string) ([]string, error) IntSlice(name string) ([]int, error) + Int64Slice(name string) ([]int64, error) Generic(name string) (cli.Generic, error) Bool(name string) (bool, error) diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index 168b6dade0..ac0532076c 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -160,6 +160,29 @@ func (x *jsonSource) IntSlice(name string) ([]int, error) { } } +func (x *jsonSource) Int64Slice(name string) ([]int64, error) { + i, err := x.getValue(name) + if err != nil { + return nil, err + } + switch v := i.(type) { + default: + return nil, fmt.Errorf("unexpected type %T for %q", i, name) + case []int64: + return v, nil + case []interface{}: + c := []int64{} + for _, s := range v { + if i2, ok := s.(int64); ok { + c = append(c, i2) + } else { + return c, fmt.Errorf("unexpected item type %T in %T for %q", s, c, name) + } + } + return c, nil + } +} + func (x *jsonSource) Generic(name string) (cli.Generic, error) { i, err := x.getValue(name) if err != nil { diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index 07de00fcc6..2505b270eb 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -207,6 +207,35 @@ func (fsm *MapInputSource) IntSlice(name string) ([]int, error) { return intSlice, nil } +// Int64Slice returns an []int64 from the map if it exists otherwise returns nil +func (fsm *MapInputSource) Int64Slice(name string) ([]int64, error) { + otherGenericValue, exists := fsm.valueMap[name] + if !exists { + otherGenericValue, exists = nestedVal(name, fsm.valueMap) + if !exists { + return nil, nil + } + } + + otherValue, isType := otherGenericValue.([]interface{}) + if !isType { + return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) + } + + var int64Slice = make([]int64, 0, len(otherValue)) + for i, v := range otherValue { + int64Value, isType := v.(int64) + + if !isType { + return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "int", v) + } + + int64Slice = append(int64Slice, int64Value) + } + + return int64Slice, nil +} + // Generic returns an cli.Generic from the map if it exists otherwise returns nil func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) { otherGenericValue, exists := fsm.valueMap[name] diff --git a/altsrc/map_input_source_test.go b/altsrc/map_input_source_test.go index b90f796b30..cf399b5354 100644 --- a/altsrc/map_input_source_test.go +++ b/altsrc/map_input_source_test.go @@ -22,3 +22,14 @@ func TestMapDuration(t *testing.T) { _, err = inputSource.Duration("duration_of_int_type") refute(t, nil, err) } + +func TestMapInputSource_Int64Slice(t *testing.T) { + inputSource := NewMapInputSource( + "test", + map[interface{}]interface{}{ + "test_num": []interface{}{int64(1), int64(2), int64(3)}, + }) + d, err := inputSource.Int64Slice("test_num") + expect(t, []int64{1, 2, 3}, d) + expect(t, nil, err) +} diff --git a/godoc-current.txt b/godoc-current.txt index 54424fb1e6..b8dbf6ad0a 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -2437,6 +2437,7 @@ type InputSourceContext interface { String(name string) (string, error) StringSlice(name string) ([]string, error) IntSlice(name string) ([]int, error) + Int64Slice(name string) ([]int64, error) Generic(name string) (cli.Generic, error) Bool(name string) (bool, error) @@ -2494,6 +2495,9 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Int64SliceFlag.Apply +func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Int64Slice value if required + type IntFlag struct { *cli.IntFlag // Has unexported fields. @@ -2554,6 +2558,10 @@ func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) func (fsm *MapInputSource) Int(name string) (int, error) Int returns an int from the map if it exists otherwise returns 0 +func (fsm *MapInputSource) Int64Slice(name string) ([]int64, error) + Int64Slice returns an []int64 from the map if it exists otherwise returns + nil + func (fsm *MapInputSource) IntSlice(name string) ([]int, error) IntSlice returns an []int from the map if it exists otherwise returns nil diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 54424fb1e6..b8dbf6ad0a 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -2437,6 +2437,7 @@ type InputSourceContext interface { String(name string) (string, error) StringSlice(name string) ([]string, error) IntSlice(name string) ([]int, error) + Int64Slice(name string) ([]int64, error) Generic(name string) (cli.Generic, error) Bool(name string) (bool, error) @@ -2494,6 +2495,9 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Int64SliceFlag.Apply +func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Int64Slice value if required + type IntFlag struct { *cli.IntFlag // Has unexported fields. @@ -2554,6 +2558,10 @@ func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) func (fsm *MapInputSource) Int(name string) (int, error) Int returns an int from the map if it exists otherwise returns 0 +func (fsm *MapInputSource) Int64Slice(name string) ([]int64, error) + Int64Slice returns an []int64 from the map if it exists otherwise returns + nil + func (fsm *MapInputSource) IntSlice(name string) ([]int, error) IntSlice returns an []int from the map if it exists otherwise returns nil