Repository: spf13/viper
Pull request #2015 (2025-04-12 09:25:00)

Add support for flags pflag.BoolSlice, pflag.UintSlice and pflag.Float64Slice
# Description 

This PR adds support for `pflag.BoolSlice`, `pflag.UintSlice` and `pflag.Float64Slice` (see failing test below).

Note: there are other pflag slices not supported, but this PR propose to add supports for at least one of each kind: Bool, Uint, & Float64.

# Extra Information

Below is a simple failing test

```golang
func TestBindPFlagsSlices(t *testing.T) {
	set := pflag.NewFlagSet("test", pflag.ContinueOnError)
        set.IntSlice("intslice", []int{}, "")
	set.BoolSlice("boolslice", []bool{}, "")
	set.Float64Slice("float64slice", []float64{}, "")
	set.UintSlice("uintslice", []uint{}, "")

	v := New()
	v.BindPFlags(set)

	// Does not fail as effectively supported
	set.Set("intslice", "1,2")
	assert.Equal(t, []int{1, 2}, v.Get("intslice"))

	// Fails as not supported
	set.Set("boolslice", "true,false")
	assert.Equal(t, []bool{true, false}, v.Get("boolslice"))

	// Fails as not supported
	set.Set("float64slice", "1.1,2.2")
	assert.Equal(t, []float64{1.1, 2.2}, v.Get("float64slice"))

	// Fails as not supported
	set.Set("uintslice", "1,2")
	assert.Equal(t, []uint{1, 2}, v.Get("uintslice"))
}
```

nmvalera: This PR is open as a draft as it requires to some methods currently not implemented in the `sf13/cast` package. I have open this [PR](https://github.com/spf13/cast/pull/236) to add support for it. Once merged I'll update this PR and open the Pull Request. 
sagikazarmark: Cast PR merged, cast version updated on master
nmvalera: @sagikazarmark This is ready for review. Thanks

Patch:
diff --git a/go.mod b/go.mod
index 4a4d700..0ffc497 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/pelletier/go-toml/v2 v2.2.4
 	github.com/sagikazarmark/locafero v0.9.0
 	github.com/spf13/afero v1.14.0
-	github.com/spf13/cast v1.8.0
+	github.com/spf13/cast v1.9.2
 	github.com/spf13/pflag v1.0.6
 	github.com/stretchr/testify v1.10.0
 	github.com/subosito/gotenv v1.6.0
diff --git a/go.sum b/go.sum
index 3057175..1e4ede8 100644
--- a/go.sum
+++ b/go.sum
@@ -25,8 +25,8 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
 github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
 github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
 github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
-github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
-github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
+github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
 github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
diff --git a/viper.go b/viper.go
index a58d757..e4bdda0 100644
--- a/viper.go
+++ b/viper.go
@@ -1181,11 +1181,26 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) any {
 			s = strings.TrimSuffix(s, "]")
 			res, _ := readAsCSV(s)
 			return res
+		case "boolSlice":
+			s := strings.TrimPrefix(flag.ValueString(), "[")
+			s = strings.TrimSuffix(s, "]")
+			res, _ := readAsCSV(s)
+			return cast.ToBoolSlice(res)
 		case "intSlice":
 			s := strings.TrimPrefix(flag.ValueString(), "[")
 			s = strings.TrimSuffix(s, "]")
 			res, _ := readAsCSV(s)
 			return cast.ToIntSlice(res)
+		case "uintSlice":
+			s := strings.TrimPrefix(flag.ValueString(), "[")
+			s = strings.TrimSuffix(s, "]")
+			res, _ := readAsCSV(s)
+			return cast.ToUintSlice(res)
+		case "float64Slice":
+			s := strings.TrimPrefix(flag.ValueString(), "[")
+			s = strings.TrimSuffix(s, "]")
+			res, _ := readAsCSV(s)
+			return cast.ToFloat64Slice(res)
 		case "durationSlice":
 			s := strings.TrimPrefix(flag.ValueString(), "[")
 			s = strings.TrimSuffix(s, "]")
@@ -1268,11 +1283,26 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) any {
 				s = strings.TrimSuffix(s, "]")
 				res, _ := readAsCSV(s)
 				return res
+			case "boolSlice":
+				s := strings.TrimPrefix(flag.ValueString(), "[")
+				s = strings.TrimSuffix(s, "]")
+				res, _ := readAsCSV(s)
+				return cast.ToBoolSlice(res)
 			case "intSlice":
 				s := strings.TrimPrefix(flag.ValueString(), "[")
 				s = strings.TrimSuffix(s, "]")
 				res, _ := readAsCSV(s)
 				return cast.ToIntSlice(res)
+			case "uintSlice":
+				s := strings.TrimPrefix(flag.ValueString(), "[")
+				s = strings.TrimSuffix(s, "]")
+				res, _ := readAsCSV(s)
+				return cast.ToUintSlice(res)
+			case "float64Slice":
+				s := strings.TrimPrefix(flag.ValueString(), "[")
+				s = strings.TrimSuffix(s, "]")
+				res, _ := readAsCSV(s)
+				return cast.ToFloat64Slice(res)
 			case "stringToString":
 				return stringToStringConv(flag.ValueString())
 			case "stringToInt":


Test patch:
diff --git a/viper_test.go b/viper_test.go
index f7e0787..e7e262b 100644
--- a/viper_test.go
+++ b/viper_test.go
@@ -1210,6 +1210,29 @@ func TestBindPFlagsStringArray(t *testing.T) {
 	}
 }
 
+func TestBindPFlagsSlices(t *testing.T) {
+	set := pflag.NewFlagSet("test", pflag.ContinueOnError)
+	set.IntSlice("intslice", []int{}, "")
+	set.BoolSlice("boolslice", []bool{}, "")
+	set.Float64Slice("float64slice", []float64{}, "")
+	set.UintSlice("uintslice", []uint{}, "")
+
+	v := New()
+	v.BindPFlags(set)
+
+	set.Set("intslice", "1,2")
+	assert.Equal(t, []int{1, 2}, v.Get("intslice"))
+
+	set.Set("boolslice", "true,false")
+	assert.Equal(t, []bool{true, false}, v.Get("boolslice"))
+
+	set.Set("float64slice", "1.1,2.2")
+	assert.Equal(t, []float64{1.1, 2.2}, v.Get("float64slice"))
+
+	set.Set("uintslice", "1,2")
+	assert.Equal(t, []uint{1, 2}, v.Get("uintslice"))
+}
+
 func TestSliceFlagsReturnCorrectType(t *testing.T) {
 	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
 	flagSet.IntSlice("int", []int{1, 2}, "")