Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SliceValue for better list semantics #216

Merged
merged 4 commits into from Sep 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 38 additions & 0 deletions bool_slice.go
Expand Up @@ -71,6 +71,44 @@ func (s *boolSliceValue) String() string {
return "[" + out + "]"
}

func (s *boolSliceValue) fromString(val string) (bool, error) {
return strconv.ParseBool(val)
}

func (s *boolSliceValue) toString(val bool) string {
return strconv.FormatBool(val)
}

func (s *boolSliceValue) Append(val string) error {
i, err := s.fromString(val)
if err != nil {
return err
}
*s.value = append(*s.value, i)
return nil
}

func (s *boolSliceValue) Replace(val []string) error {
out := make([]bool, len(val))
for i, d := range val {
var err error
out[i], err = s.fromString(d)
if err != nil {
return err
}
}
*s.value = out
return nil
}

func (s *boolSliceValue) GetSlice() []string {
out := make([]string, len(*s.value))
for i, d := range *s.value {
out[i] = s.toString(d)
}
return out
}

func boolSliceConv(val string) (interface{}, error) {
val = strings.Trim(val, "[]")
// Empty string would cause a slice with one (empty) entry
Expand Down
23 changes: 23 additions & 0 deletions bool_slice_test.go
Expand Up @@ -160,6 +160,29 @@ func TestBSCalledTwice(t *testing.T) {
}
}

func TestBSAsSliceValue(t *testing.T) {
var bs []bool
f := setUpBSFlagSet(&bs)

in := []string{"true", "false"}
argfmt := "--bs=%s"
arg1 := fmt.Sprintf(argfmt, in[0])
arg2 := fmt.Sprintf(argfmt, in[1])
err := f.Parse([]string{arg1, arg2})
if err != nil {
t.Fatal("expected no error; got", err)
}

f.VisitAll(func(f *Flag) {
if val, ok := f.Value.(SliceValue); ok {
_ = val.Replace([]string{"false"})
}
})
if len(bs) != 1 || bs[0] != false {
t.Fatalf("Expected ss to be overwritten with 'false', but got: %v", bs)
}
}

func TestBSBadQuoting(t *testing.T) {

tests := []struct {
Expand Down
38 changes: 38 additions & 0 deletions duration_slice.go
Expand Up @@ -51,6 +51,44 @@ func (s *durationSliceValue) String() string {
return "[" + strings.Join(out, ",") + "]"
}

func (s *durationSliceValue) fromString(val string) (time.Duration, error) {
return time.ParseDuration(val)
}

func (s *durationSliceValue) toString(val time.Duration) string {
return fmt.Sprintf("%s", val)
}

func (s *durationSliceValue) Append(val string) error {
i, err := s.fromString(val)
if err != nil {
return err
}
*s.value = append(*s.value, i)
return nil
}

func (s *durationSliceValue) Replace(val []string) error {
out := make([]time.Duration, len(val))
for i, d := range val {
var err error
out[i], err = s.fromString(d)
if err != nil {
return err
}
}
*s.value = out
return nil
}

func (s *durationSliceValue) GetSlice() []string {
out := make([]string, len(*s.value))
for i, d := range *s.value {
out[i] = s.toString(d)
}
return out
}

func durationSliceConv(val string) (interface{}, error) {
val = strings.Trim(val, "[]")
// Empty string would cause a slice with one (empty) entry
Expand Down
23 changes: 23 additions & 0 deletions duration_slice_test.go
Expand Up @@ -144,6 +144,29 @@ func TestDSWithDefault(t *testing.T) {
}
}

func TestDSAsSliceValue(t *testing.T) {
var ds []time.Duration
f := setUpDSFlagSet(&ds)

in := []string{"1ns", "2ns"}
argfmt := "--ds=%s"
arg1 := fmt.Sprintf(argfmt, in[0])
arg2 := fmt.Sprintf(argfmt, in[1])
err := f.Parse([]string{arg1, arg2})
if err != nil {
t.Fatal("expected no error; got", err)
}

f.VisitAll(func(f *Flag) {
if val, ok := f.Value.(SliceValue); ok {
_ = val.Replace([]string{"3ns"})
}
})
if len(ds) != 1 || ds[0] != time.Duration(3) {
t.Fatalf("Expected ss to be overwritten with '3ns', but got: %v", ds)
}
}

func TestDSCalledTwice(t *testing.T) {
var ds []time.Duration
f := setUpDSFlagSet(&ds)
Expand Down
12 changes: 12 additions & 0 deletions flag.go
Expand Up @@ -190,6 +190,18 @@ type Value interface {
Type() string
}

// SliceValue is a secondary interface to all flags which hold a list
// of values. This allows full control over the value of list flags,
// and avoids complicated marshalling and unmarshalling to csv.
type SliceValue interface {
// Append adds the specified value to the end of the flag value list.
Append(string) error
// Replace will fully overwrite any data currently in the flag value list.
Replace([]string) error
// GetSlice returns the flag value list as an array of strings.
GetSlice() []string
}

// sortFlags returns the flags as a slice in lexicographical sorted order.
func sortFlags(flags map[NormalizedName]*Flag) []*Flag {
list := make(sort.StringSlice, len(flags))
Expand Down
42 changes: 42 additions & 0 deletions float32_slice.go
Expand Up @@ -53,6 +53,48 @@ func (s *float32SliceValue) String() string {
return "[" + strings.Join(out, ",") + "]"
}

func (s *float32SliceValue) fromString(val string) (float32, error) {
t64, err := strconv.ParseFloat(val, 32)
if err != nil {
return 0, err
}
return float32(t64), nil
}

func (s *float32SliceValue) toString(val float32) string {
return fmt.Sprintf("%f", val)
}

func (s *float32SliceValue) Append(val string) error {
i, err := s.fromString(val)
if err != nil {
return err
}
*s.value = append(*s.value, i)
return nil
}

func (s *float32SliceValue) Replace(val []string) error {
out := make([]float32, len(val))
for i, d := range val {
var err error
out[i], err = s.fromString(d)
if err != nil {
return err
}
}
*s.value = out
return nil
}

func (s *float32SliceValue) GetSlice() []string {
out := make([]string, len(*s.value))
for i, d := range *s.value {
out[i] = s.toString(d)
}
return out
}

func float32SliceConv(val string) (interface{}, error) {
val = strings.Trim(val, "[]")
// Empty string would cause a slice with one (empty) entry
Expand Down
23 changes: 23 additions & 0 deletions float32_slice_test.go
Expand Up @@ -156,6 +156,29 @@ func TestF32SWithDefault(t *testing.T) {
}
}

func TestF32SAsSliceValue(t *testing.T) {
var f32s []float32
f := setUpF32SFlagSet(&f32s)

in := []string{"1.0", "2.0"}
argfmt := "--f32s=%s"
arg1 := fmt.Sprintf(argfmt, in[0])
arg2 := fmt.Sprintf(argfmt, in[1])
err := f.Parse([]string{arg1, arg2})
if err != nil {
t.Fatal("expected no error; got", err)
}

f.VisitAll(func(f *Flag) {
if val, ok := f.Value.(SliceValue); ok {
_ = val.Replace([]string{"3.1"})
}
})
if len(f32s) != 1 || f32s[0] != 3.1 {
t.Fatalf("Expected ss to be overwritten with '3.1', but got: %v", f32s)
}
}

func TestF32SCalledTwice(t *testing.T) {
var f32s []float32
f := setUpF32SFlagSet(&f32s)
Expand Down
38 changes: 38 additions & 0 deletions float64_slice.go
Expand Up @@ -51,6 +51,44 @@ func (s *float64SliceValue) String() string {
return "[" + strings.Join(out, ",") + "]"
}

func (s *float64SliceValue) fromString(val string) (float64, error) {
return strconv.ParseFloat(val, 64)
}

func (s *float64SliceValue) toString(val float64) string {
return fmt.Sprintf("%f", val)
}

func (s *float64SliceValue) Append(val string) error {
i, err := s.fromString(val)
if err != nil {
return err
}
*s.value = append(*s.value, i)
return nil
}

func (s *float64SliceValue) Replace(val []string) error {
out := make([]float64, len(val))
for i, d := range val {
var err error
out[i], err = s.fromString(d)
if err != nil {
return err
}
}
*s.value = out
return nil
}

func (s *float64SliceValue) GetSlice() []string {
out := make([]string, len(*s.value))
for i, d := range *s.value {
out[i] = s.toString(d)
}
return out
}

func float64SliceConv(val string) (interface{}, error) {
val = strings.Trim(val, "[]")
// Empty string would cause a slice with one (empty) entry
Expand Down
23 changes: 23 additions & 0 deletions float64_slice_test.go
Expand Up @@ -144,6 +144,29 @@ func TestF64SWithDefault(t *testing.T) {
}
}

func TestF64SAsSliceValue(t *testing.T) {
var f64s []float64
f := setUpF64SFlagSet(&f64s)

in := []string{"1.0", "2.0"}
argfmt := "--f64s=%s"
arg1 := fmt.Sprintf(argfmt, in[0])
arg2 := fmt.Sprintf(argfmt, in[1])
err := f.Parse([]string{arg1, arg2})
if err != nil {
t.Fatal("expected no error; got", err)
}

f.VisitAll(func(f *Flag) {
if val, ok := f.Value.(SliceValue); ok {
_ = val.Replace([]string{"3.1"})
}
})
if len(f64s) != 1 || f64s[0] != 3.1 {
t.Fatalf("Expected ss to be overwritten with '3.1', but got: %v", f64s)
}
}

func TestF64SCalledTwice(t *testing.T) {
var f64s []float64
f := setUpF64SFlagSet(&f64s)
Expand Down
5 changes: 5 additions & 0 deletions go.mod
@@ -0,0 +1,5 @@
module github.com/spf13/pflags

go 1.12

require github.com/spf13/pflag v1.0.3
2 changes: 2 additions & 0 deletions go.sum
@@ -0,0 +1,2 @@
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
42 changes: 42 additions & 0 deletions int32_slice.go
Expand Up @@ -53,6 +53,48 @@ func (s *int32SliceValue) String() string {
return "[" + strings.Join(out, ",") + "]"
}

func (s *int32SliceValue) fromString(val string) (int32, error) {
t64, err := strconv.ParseInt(val, 0, 32)
if err != nil {
return 0, err
}
return int32(t64), nil
}

func (s *int32SliceValue) toString(val int32) string {
return fmt.Sprintf("%d", val)
}

func (s *int32SliceValue) Append(val string) error {
i, err := s.fromString(val)
if err != nil {
return err
}
*s.value = append(*s.value, i)
return nil
}

func (s *int32SliceValue) Replace(val []string) error {
out := make([]int32, len(val))
for i, d := range val {
var err error
out[i], err = s.fromString(d)
if err != nil {
return err
}
}
*s.value = out
return nil
}

func (s *int32SliceValue) GetSlice() []string {
out := make([]string, len(*s.value))
for i, d := range *s.value {
out[i] = s.toString(d)
}
return out
}

func int32SliceConv(val string) (interface{}, error) {
val = strings.Trim(val, "[]")
// Empty string would cause a slice with one (empty) entry
Expand Down