Repository: urfave/cli
Pull request #1551 (2022-10-28 11:49:53)

Feature:(issue_1550):Add support Int64Slice by read toml config file
<!--
  This template provides some ideas of things to include in your PR description.
  To start, try providing a short summary of your changes in the Title above.
  If a section of the PR template does not apply to this PR, then delete that section.
 -->

## What type of PR is this?

_(REQUIRED)_

<!--
  Delete any of the following that do not apply:
 -->

- bug
- cleanup
- documentation
- feature

## What this PR does / why we need it:

_(REQUIRED)_

<!--
  What goal is this change working towards?
  Provide a bullet pointed summary of how each file was changed.
  Briefly explain any decisions you made with respect to the changes.
  Include anything here that you didn't include in *Release Notes*
  above, such as changes to CI or changes to internal methods.
-->

## Which issue(s) this PR fixes:

_(REQUIRED)_

<!--
If this PR fixes one of more issues, list them here.
One line each, like so:

Fixes #123
Fixes #39
-->

## Special notes for your reviewer:

_(fill-in or delete this section)_

<!--
   Is there any particular feedback you would / wouldn't like?
   Which parts of the code should reviewers focus on?
-->

## Testing

_(fill-in or delete this section)_

<!--
  Describe how you tested this change.
-->

## Release Notes

_(REQUIRED)_
<!--
  If this PR makes user facing changes, please describe them here. This
  description will be copied into the release notes/changelog, whenever the
  next version is released. Keep this section short, and focus on high level
  changes.

  Put your text between the block. To omit notes, use NONE within the block.
-->

```release-note

```


Edelweiss-Snow: > @Edelweiss-Snow Can you run make in your local workspace ? You might have to do a make v2approve to get past this error.

I can run the program local workspace, I try to solve this error

Patch:
diff --git a/altsrc/flag.go b/altsrc/flag.go
index 8697b29..d8256d7 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/input_source_context.go b/altsrc/input_source_context.go
index d743253..24579d1 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 168b6da..ac05320 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 07de00f..2505b27 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/godoc-current.txt b/godoc-current.txt
index 54424fb..b8dbf6a 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
 


Test patch:
diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go
index caf62d8..ad66dab 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/map_input_source_test.go b/altsrc/map_input_source_test.go
index b90f796..cf399b5 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/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt
index 54424fb..b8dbf6a 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