Skip to content

Commit

Permalink
Merge pull request #216 from therealmitchconnors/elegant
Browse files Browse the repository at this point in the history
Implement SliceValue for better list semantics
  • Loading branch information
therealmitchconnors committed Sep 17, 2019
2 parents 9722382 + 8e39cc4 commit 7b22f68
Show file tree
Hide file tree
Showing 24 changed files with 635 additions and 0 deletions.
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

0 comments on commit 7b22f68

Please sign in to comment.