-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add KeyArrayValue to flagx * More tests * Use reflect to compare maps * Sort strings * Review comments * Use SplitN
- Loading branch information
1 parent
30ff4b3
commit 3b58f84
Showing
2 changed files
with
148 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
}) | ||
} | ||
} |