Skip to content

Commit

Permalink
Add KeyArrayValue to flagx (#145)
Browse files Browse the repository at this point in the history
* Add KeyArrayValue to flagx

* More tests

* Use reflect to compare maps

* Sort strings

* Review comments

* Use SplitN
  • Loading branch information
cristinaleonr authored Jun 10, 2022
1 parent 30ff4b3 commit 3b58f84
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
57 changes: 57 additions & 0 deletions flagx/keyvaluearray.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package flagx

import (
"fmt"
"strings"
)

// KeyValueArray maps a key to one or multiple values.
// It parses "key=value1,value2,..." pairs from a given argument
// and is designed to be used for repeatable arguments.
// Each use of the flag will add a new key-[]value pair
// (or append to a previous one if the same key is used).
type KeyValueArray struct {
pairs map[string][]string
}

// Set parses key=value1,value2,... arguments. Only one key-[]value
// pair can be specified per call.
func (kva *KeyValueArray) Set(pair string) error {
p := strings.SplitN(pair, "=", 2)
if len(p) != 2 {
return fmt.Errorf("bad input pair: %s split on '=' into %d pieces (should have been 2)",
pair, len(p))
}
if kva.pairs == nil {
kva.pairs = make(map[string][]string)
}
v := strings.Split(p[1], ",")
if _, ok := kva.pairs[p[0]]; !ok {
kva.pairs[p[0]] = make([]string, 0)
}
kva.pairs[p[0]] = append(kva.pairs[p[0]], v...)
return nil
}

// String returns the key-[]value pairs as a string.
func (kva *KeyValueArray) String() string {
var sb strings.Builder
for k, v := range kva.pairs {
joined := strings.Join(v, ",")
sb.WriteString(fmt.Sprintf("%s:[%s],", k, joined))
}
s := sb.String()
return strings.TrimSuffix(s, ",")
}

// Get returns all of the KeyValueArray pairs as a map[string][]string.
// The returned value is a copy.
func (kva *KeyValueArray) Get() map[string][]string {
h := make(map[string][]string)
for k, s := range kva.pairs {
c := make([]string, len(s))
copy(c, s)
h[k] = c
}
return h
}
91 changes: 91 additions & 0 deletions flagx/keyvaluearray_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package flagx_test

import (
"reflect"
"sort"
"strings"
"testing"

"github.com/go-test/deep"
"github.com/m-lab/go/flagx"
)

func TestKeyValueArray(t *testing.T) {
tests := []struct {
name string
flags []string
want map[string][]string
wantErr bool
wantString string
}{
{
name: "success-single-pair-one-value",
flags: []string{"key1=value1"},
want: map[string][]string{
"key1": []string{"value1"},
},
wantErr: false,
wantString: "key1:[value1]",
},
{
name: "success-single-pair-multiple-values",
flags: []string{"key1=value1,value1.1"},
want: map[string][]string{
"key1": []string{"value1", "value1.1"},
},
wantErr: false,
wantString: "key1:[value1,value1.1]",
},
{
name: "success-multiple-pairs",
flags: []string{
"key1=value1,value1.1",
"key2=value2,value2.1,value2.2",
"key3=value3",
"key2=value2.3",
},
want: map[string][]string{
"key1": []string{"value1", "value1.1"},
"key2": []string{"value2", "value2.1", "value2.2", "value2.3"},
"key3": []string{"value3"},
},
wantErr: false,
wantString: "key1:[value1,value1.1],key2:[value2,value2.1,value2.2,value2.3],key3:[value3]",
},
{
name: "invalid-input",
flags: []string{"key1"},
want: map[string][]string{},
wantErr: true,
wantString: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
kva := flagx.KeyValueArray{}
for _, f := range tt.flags {
if err := kva.Set(f); (err != nil) != tt.wantErr {
t.Errorf("KeyValueArrray.Set() error: %v, wantErr: %v", err, tt.wantErr)
}
}
if tt.wantErr {
return
}

got := kva.Get()
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("KeyValueArray.Get() did not match; got: %v, want: %v", got, tt.want)
}

// Sort because order is not guaranteed.
strFields := strings.Split(kva.String(), ",")
sort.Strings(strFields)
kvsFields := strings.Split(tt.wantString, ",")
sort.Strings(kvsFields)
if diff := deep.Equal(strFields, kvsFields); diff != nil {
t.Errorf("KeyValueArray.String() did not match; got = %v, want %v, diff %v", strFields, kvsFields, diff)
}
})
}
}

0 comments on commit 3b58f84

Please sign in to comment.