add a new type StringArray
diff --git a/flag.go b/flag.go
index eb143d7..c28f3bd 100644
--- a/flag.go
+++ b/flag.go
@@ -434,7 +434,7 @@
return f.DefValue == ""
case *ipValue, *ipMaskValue, *ipNetValue:
return f.DefValue == "<nil>"
- case *intSliceValue, *stringSliceValue:
+ case *intSliceValue, *stringSliceValue, *stringArrayValue:
return f.DefValue == "[]"
default:
return true
diff --git a/flag_test.go b/flag_test.go
index 86b37e5..1befb94 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -874,23 +874,24 @@
}
}
-const defaultOutput = ` --A for bootstrapping, allow 'any' type
- --Alongflagname disable bounds checking
- -C, --CCC a boolean defaulting to true (default true)
- --D path set relative path for local imports
- --F number a non-zero number (default 2.7)
- --G float a float that defaults to zero
- --IP ip IP address with no default
- --IPMask ipMask Netmask address with no default
- --IPNet ipNet IP network with no default
- --Ints intSlice int slice with zero default
- --N int a non-zero int (default 27)
- --ND1 string[="bar"] a string with NoOptDefVal (default "foo")
- --ND2 num[=4321] a num with NoOptDefVal (default 1234)
- --Strings stringSlice string slice with zero default
- --Z int an int that defaults to zero
- --custom custom custom Value implementation
- --maxT timeout set timeout for dial
+const defaultOutput = ` --A for bootstrapping, allow 'any' type
+ --Alongflagname disable bounds checking
+ -C, --CCC a boolean defaulting to true (default true)
+ --D path set relative path for local imports
+ --F number a non-zero number (default 2.7)
+ --G float a float that defaults to zero
+ --IP ip IP address with no default
+ --IPMask ipMask Netmask address with no default
+ --IPNet ipNet IP network with no default
+ --Ints intSlice int slice with zero default
+ --N int a non-zero int (default 27)
+ --ND1 string[="bar"] a string with NoOptDefVal (default "foo")
+ --ND2 num[=4321] a num with NoOptDefVal (default 1234)
+ --StringArray stringArray string array with zero default
+ --StringSlice stringSlice string slice with zero default
+ --Z int an int that defaults to zero
+ --custom custom custom Value implementation
+ --maxT timeout set timeout for dial
`
// Custom value that satisfies the Value interface.
@@ -927,7 +928,8 @@
fs.Lookup("ND1").NoOptDefVal = "bar"
fs.Int("ND2", 1234, "a `num` with NoOptDefVal")
fs.Lookup("ND2").NoOptDefVal = "4321"
- fs.StringSlice("Strings", []string{}, "string slice with zero default")
+ fs.StringSlice("StringSlice", []string{}, "string slice with zero default")
+ fs.StringArray("StringArray", []string{}, "string array with zero default")
var cv customValue
fs.Var(&cv, "custom", "custom Value implementation")
diff --git a/string_array.go b/string_array.go
new file mode 100644
index 0000000..f320f2e
--- /dev/null
+++ b/string_array.go
@@ -0,0 +1,110 @@
+package pflag
+
+import (
+ "fmt"
+ "strings"
+)
+
+var _ = fmt.Fprint
+
+// -- stringArray Value
+type stringArrayValue struct {
+ value *[]string
+ changed bool
+}
+
+func newStringArrayValue(val []string, p *[]string) *stringArrayValue {
+ ssv := new(stringArrayValue)
+ ssv.value = p
+ *ssv.value = val
+ return ssv
+}
+
+func (s *stringArrayValue) Set(val string) error {
+ if !s.changed {
+ *s.value = []string{val}
+ s.changed = true
+ } else {
+ *s.value = append(*s.value, val)
+ }
+ return nil
+}
+
+func (s *stringArrayValue) Type() string {
+ return "stringArray"
+}
+
+func (s *stringArrayValue) String() string {
+ str, _ := writeAsCSV(*s.value)
+ return "[" + str + "]"
+}
+
+func stringArrayConv(sval string) (interface{}, error) {
+ sval = strings.Trim(sval, "[]")
+ // An empty string would cause a array with one (empty) string
+ if len(sval) == 0 {
+ return []string{}, nil
+ }
+ return readAsCSV(sval)
+}
+
+// GetStringArray return the []string value of a flag with the given name
+func (f *FlagSet) GetStringArray(name string) ([]string, error) {
+ val, err := f.getFlagType(name, "stringArray", stringArrayConv)
+ if err != nil {
+ return []string{}, err
+ }
+ return val.([]string), nil
+}
+
+// StringArrayVar defines a string flag with specified name, default value, and usage string.
+// The argument p points to a []string variable in which to store the values of the multiple flags.
+// The value of each argument will not try to be separated by comma
+func (f *FlagSet) StringArrayVar(p *[]string, name string, value []string, usage string) {
+ f.VarP(newStringArrayValue(value, p), name, "", usage)
+}
+
+// StringArrayVarP is like StringArrayVar, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) StringArrayVarP(p *[]string, name, shorthand string, value []string, usage string) {
+ f.VarP(newStringArrayValue(value, p), name, shorthand, usage)
+}
+
+// StringArrayVar defines a string flag with specified name, default value, and usage string.
+// The argument p points to a []string variable in which to store the value of the flag.
+// The value of each argument will not try to be separated by comma
+func StringArrayVar(p *[]string, name string, value []string, usage string) {
+ CommandLine.VarP(newStringArrayValue(value, p), name, "", usage)
+}
+
+// StringArrayVarP is like StringArrayVar, but accepts a shorthand letter that can be used after a single dash.
+func StringArrayVarP(p *[]string, name, shorthand string, value []string, usage string) {
+ CommandLine.VarP(newStringArrayValue(value, p), name, shorthand, usage)
+}
+
+// StringArray defines a string flag with specified name, default value, and usage string.
+// The return value is the address of a []string variable that stores the value of the flag.
+// The value of each argument will not try to be separated by comma
+func (f *FlagSet) StringArray(name string, value []string, usage string) *[]string {
+ p := []string{}
+ f.StringArrayVarP(&p, name, "", value, usage)
+ return &p
+}
+
+// StringArrayP is like StringArray, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) StringArrayP(name, shorthand string, value []string, usage string) *[]string {
+ p := []string{}
+ f.StringArrayVarP(&p, name, shorthand, value, usage)
+ return &p
+}
+
+// StringArray defines a string flag with specified name, default value, and usage string.
+// The return value is the address of a []string variable that stores the value of the flag.
+// The value of each argument will not try to be separated by comma
+func StringArray(name string, value []string, usage string) *[]string {
+ return CommandLine.StringArrayP(name, "", value, usage)
+}
+
+// StringArrayP is like StringArray, but accepts a shorthand letter that can be used after a single dash.
+func StringArrayP(name, shorthand string, value []string, usage string) *[]string {
+ return CommandLine.StringArrayP(name, shorthand, value, usage)
+}
diff --git a/string_array_test.go b/string_array_test.go
new file mode 100644
index 0000000..3e3eb74
--- /dev/null
+++ b/string_array_test.go
@@ -0,0 +1,194 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pflag
+
+import (
+ "fmt"
+ "testing"
+)
+
+func setUpSAFlagSet(sap *[]string) *FlagSet {
+ f := NewFlagSet("test", ContinueOnError)
+ f.StringArrayVar(sap, "sa", []string{}, "Command separated list!")
+ return f
+}
+
+func setUpSAFlagSetWithDefault(sap *[]string) *FlagSet {
+ f := NewFlagSet("test", ContinueOnError)
+ f.StringArrayVar(sap, "sa", []string{"default", "values"}, "Command separated list!")
+ return f
+}
+
+func TestEmptySA(t *testing.T) {
+ var sa []string
+ f := setUpSAFlagSet(&sa)
+ err := f.Parse([]string{})
+ if err != nil {
+ t.Fatal("expected no error; got", err)
+ }
+
+ getSA, err := f.GetStringArray("sa")
+ if err != nil {
+ t.Fatal("got an error from GetStringArray():", err)
+ }
+ if len(getSA) != 0 {
+ t.Fatalf("got sa %v with len=%d but expected length=0", getSA, len(getSA))
+ }
+}
+
+func TestEmptySAValue(t *testing.T) {
+ var sa []string
+ f := setUpSAFlagSet(&sa)
+ err := f.Parse([]string{"--sa="})
+ if err != nil {
+ t.Fatal("expected no error; got", err)
+ }
+
+ getSA, err := f.GetStringArray("sa")
+ if err != nil {
+ t.Fatal("got an error from GetStringArray():", err)
+ }
+ if len(getSA) != 0 {
+ t.Fatalf("got sa %v with len=%d but expected length=0", getSA, len(getSA))
+ }
+}
+
+func TestSADefault(t *testing.T) {
+ var sa []string
+ f := setUpSAFlagSetWithDefault(&sa)
+
+ vals := []string{"default", "values"}
+
+ err := f.Parse([]string{})
+ if err != nil {
+ t.Fatal("expected no error; got", err)
+ }
+ for i, v := range sa {
+ if vals[i] != v {
+ t.Fatalf("expected sa[%d] to be %s but got: %s", i, vals[i], v)
+ }
+ }
+
+ getSA, err := f.GetStringArray("sa")
+ if err != nil {
+ t.Fatal("got an error from GetStringArray():", err)
+ }
+ for i, v := range getSA {
+ if vals[i] != v {
+ t.Fatalf("expected sa[%d] to be %s from GetStringArray but got: %s", i, vals[i], v)
+ }
+ }
+}
+
+func TestSAWithDefault(t *testing.T) {
+ var sa []string
+ f := setUpSAFlagSetWithDefault(&sa)
+
+ val := "one"
+ arg := fmt.Sprintf("--sa=%s", val)
+ err := f.Parse([]string{arg})
+ if err != nil {
+ t.Fatal("expected no error; got", err)
+ }
+
+ if len(sa) != 1 {
+ t.Fatalf("expected number of values to be %d but %d", 1, len(sa))
+ }
+
+ if sa[0] != val {
+ t.Fatalf("expected value to be %s but got: %s", sa[0], val)
+ }
+
+ getSA, err := f.GetStringArray("sa")
+ if err != nil {
+ t.Fatal("got an error from GetStringArray():", err)
+ }
+
+ if len(getSA) != 1 {
+ t.Fatalf("expected number of values to be %d but %d", 1, len(getSA))
+ }
+
+ if getSA[0] != val {
+ t.Fatalf("expected value to be %s but got: %s", getSA[0], val)
+ }
+}
+
+func TestSACalledTwice(t *testing.T) {
+ var sa []string
+ f := setUpSAFlagSet(&sa)
+
+ in := []string{"one", "two"}
+ expected := []string{"one", "two"}
+ argfmt := "--sa=%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)
+ }
+
+ if len(expected) != len(sa) {
+ t.Fatalf("expected number of sa to be %d but got: %d", len(expected), len(sa))
+ }
+ for i, v := range sa {
+ if expected[i] != v {
+ t.Fatalf("expected sa[%d] to be %s but got: %s", i, expected[i], v)
+ }
+ }
+
+ values, err := f.GetStringArray("sa")
+ if err != nil {
+ t.Fatal("expected no error; got", err)
+ }
+
+ if len(expected) != len(values) {
+ t.Fatalf("expected number of values to be %d but got: %d", len(expected), len(sa))
+ }
+ for i, v := range values {
+ if expected[i] != v {
+ t.Fatalf("expected got sa[%d] to be %s but got: %s", i, expected[i], v)
+ }
+ }
+}
+
+func TestSAWithSpecialChar(t *testing.T) {
+ var sa []string
+ f := setUpSAFlagSet(&sa)
+
+ in := []string{"one,two", `"three"`, `"four,five",six`, "seven eight"}
+ expected := []string{"one,two", `"three"`, `"four,five",six`, "seven eight"}
+ argfmt := "--sa=%s"
+ arg1 := fmt.Sprintf(argfmt, in[0])
+ arg2 := fmt.Sprintf(argfmt, in[1])
+ arg3 := fmt.Sprintf(argfmt, in[2])
+ arg4 := fmt.Sprintf(argfmt, in[3])
+ err := f.Parse([]string{arg1, arg2, arg3, arg4})
+ if err != nil {
+ t.Fatal("expected no error; got", err)
+ }
+
+ if len(expected) != len(sa) {
+ t.Fatalf("expected number of sa to be %d but got: %d", len(expected), len(sa))
+ }
+ for i, v := range sa {
+ if expected[i] != v {
+ t.Fatalf("expected sa[%d] to be %s but got: %s", i, expected[i], v)
+ }
+ }
+
+ values, err := f.GetStringArray("sa")
+ if err != nil {
+ t.Fatal("expected no error; got", err)
+ }
+
+ if len(expected) != len(values) {
+ t.Fatalf("expected number of values to be %d but got: %d", len(expected), len(values))
+ }
+ for i, v := range values {
+ if expected[i] != v {
+ t.Fatalf("expected got sa[%d] to be %s but got: %s", i, expected[i], v)
+ }
+ }
+}
diff --git a/string_slice.go b/string_slice.go
index 927a440..51e3c5d 100644
--- a/string_slice.go
+++ b/string_slice.go
@@ -31,6 +31,17 @@
return csvReader.Read()
}
+func writeAsCSV(vals []string) (string, error) {
+ b := &bytes.Buffer{}
+ w := csv.NewWriter(b)
+ err := w.Write(vals)
+ if err != nil {
+ return "", err
+ }
+ w.Flush()
+ return strings.TrimSuffix(b.String(), fmt.Sprintln()), nil
+}
+
func (s *stringSliceValue) Set(val string) error {
v, err := readAsCSV(val)
if err != nil {
@@ -50,11 +61,8 @@
}
func (s *stringSliceValue) String() string {
- b := &bytes.Buffer{}
- w := csv.NewWriter(b)
- w.Write(*s.value)
- w.Flush()
- return "[" + strings.TrimSuffix(b.String(), fmt.Sprintln()) + "]"
+ str, _ := writeAsCSV(*s.value)
+ return "[" + str + "]"
}
func stringSliceConv(sval string) (interface{}, error) {