Merge pull request #88 from bogem/fixes
Some minor fixes
diff --git a/.travis.yml b/.travis.yml
index 580ad22..0a7c136 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,8 +5,12 @@
go:
- 1.5.4
- 1.6.3
+ - 1.7
- tip
+matrix:
+ allow_failures:
+ - go: tip
install:
- go get github.com/golang/lint/golint
- export PATH=$GOPATH/bin:$PATH
diff --git a/flag.go b/flag.go
index 965df13..eb143d7 100644
--- a/flag.go
+++ b/flag.go
@@ -419,20 +419,26 @@
fmt.Fprintf(f.out(), "%s", usages)
}
-// isZeroValue guesses whether the string represents the zero
-// value for a flag. It is not accurate but in practice works OK.
-func isZeroValue(value string) bool {
- switch value {
- case "false":
- return true
- case "<nil>":
- return true
- case "":
- return true
- case "0":
+// defaultIsZeroValue returns true if the default value for this flag represents
+// a zero value.
+func (f *Flag) defaultIsZeroValue() bool {
+ switch f.Value.(type) {
+ case boolFlag:
+ return f.DefValue == "false"
+ case *durationValue:
+ // Beginning in Go 1.7, duration zero values are "0s"
+ return f.DefValue == "0" || f.DefValue == "0s"
+ case *intValue, *int8Value, *int32Value, *int64Value, *uintValue, *uint8Value, *uint16Value, *uint32Value, *uint64Value, *countValue, *float32Value, *float64Value:
+ return f.DefValue == "0"
+ case *stringValue:
+ return f.DefValue == ""
+ case *ipValue, *ipMaskValue, *ipNetValue:
+ return f.DefValue == "<nil>"
+ case *intSliceValue, *stringSliceValue:
+ return f.DefValue == "[]"
+ default:
return true
}
- return false
}
// UnquoteUsage extracts a back-quoted name from the usage
@@ -455,22 +461,19 @@
break // Only one back quote; use type name.
}
}
- // No explicit name, so use type if we can find one.
- name = "value"
- switch flag.Value.(type) {
- case boolFlag:
+
+ name = flag.Value.Type()
+ switch name {
+ case "bool":
name = ""
- case *durationValue:
- name = "duration"
- case *float64Value:
+ case "float64":
name = "float"
- case *intValue, *int64Value:
+ case "int64":
name = "int"
- case *stringValue:
- name = "string"
- case *uintValue, *uint64Value:
+ case "uint64":
name = "uint"
}
+
return
}
@@ -519,7 +522,7 @@
}
line += usage
- if !isZeroValue(flag.DefValue) {
+ if !flag.defaultIsZeroValue() {
if flag.Value.Type() == "string" {
line += fmt.Sprintf(" (default %q)", flag.DefValue)
} else {
diff --git a/flag_test.go b/flag_test.go
index 0ae2e4f..86b37e5 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -13,6 +13,7 @@
"os"
"reflect"
"sort"
+ "strconv"
"strings"
"testing"
"time"
@@ -873,19 +874,38 @@
}
}
-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
- --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)
- --Z int an int that defaults to zero
- --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)
+ --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
`
+// Custom value that satisfies the Value interface.
+type customValue int
+
+func (cv *customValue) String() string { return fmt.Sprintf("%v", *cv) }
+
+func (cv *customValue) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 64)
+ *cv = customValue(v)
+ return err
+}
+
+func (cv *customValue) Type() string { return "custom" }
+
func TestPrintDefaults(t *testing.T) {
fs := NewFlagSet("print defaults test", ContinueOnError)
var buf bytes.Buffer
@@ -897,12 +917,21 @@
fs.Float64("F", 2.7, "a non-zero `number`")
fs.Float64("G", 0, "a float that defaults to zero")
fs.Int("N", 27, "a non-zero int")
+ fs.IntSlice("Ints", []int{}, "int slice with zero default")
+ fs.IP("IP", nil, "IP address with no default")
+ fs.IPMask("IPMask", nil, "Netmask address with no default")
+ fs.IPNet("IPNet", net.IPNet{}, "IP network with no default")
fs.Int("Z", 0, "an int that defaults to zero")
fs.Duration("maxT", 0, "set `timeout` for dial")
fs.String("ND1", "foo", "a string with NoOptDefVal")
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")
+
+ var cv customValue
+ fs.Var(&cv, "custom", "custom Value implementation")
+
fs.PrintDefaults()
got := buf.String()
if got != defaultOutput {
diff --git a/string_slice.go b/string_slice.go
index f2a49d9..927a440 100644
--- a/string_slice.go
+++ b/string_slice.go
@@ -1,6 +1,7 @@
package pflag
import (
+ "bytes"
"encoding/csv"
"fmt"
"strings"
@@ -48,7 +49,13 @@
return "stringSlice"
}
-func (s *stringSliceValue) String() string { return "[" + strings.Join(*s.value, ",") + "]" }
+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()) + "]"
+}
func stringSliceConv(sval string) (interface{}, error) {
sval = strings.Trim(sval, "[]")
@@ -56,8 +63,7 @@
if len(sval) == 0 {
return []string{}, nil
}
- v := strings.Split(sval, ",")
- return v, nil
+ return readAsCSV(sval)
}
// GetStringSlice return the []string value of a flag with the given name
diff --git a/string_slice_test.go b/string_slice_test.go
index ed7cbfc..26118c7 100644
--- a/string_slice_test.go
+++ b/string_slice_test.go
@@ -150,29 +150,66 @@
if err != nil {
t.Fatal("expected no error; got", err)
}
+
+ if len(expected) != len(ss) {
+ t.Fatalf("expected number of ss to be %d but got: %d", len(expected), len(ss))
+ }
for i, v := range ss {
if expected[i] != v {
t.Fatalf("expected ss[%d] to be %s but got: %s", i, expected[i], v)
}
}
+
+ values, err := f.GetStringSlice("ss")
+ 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(ss))
+ }
+ for i, v := range values {
+ if expected[i] != v {
+ t.Fatalf("expected got ss[%d] to be %s but got: %s", i, expected[i], v)
+ }
+ }
}
func TestSSWithComma(t *testing.T) {
var ss []string
f := setUpSSFlagSet(&ss)
- in := []string{`"one,two"`, `"three"`}
- expected := []string{"one,two", "three"}
+ in := []string{`"one,two"`, `"three"`, `"four,five",six`}
+ expected := []string{"one,two", "three", "four,five", "six"}
argfmt := "--ss=%s"
arg1 := fmt.Sprintf(argfmt, in[0])
arg2 := fmt.Sprintf(argfmt, in[1])
- err := f.Parse([]string{arg1, arg2})
+ arg3 := fmt.Sprintf(argfmt, in[2])
+ err := f.Parse([]string{arg1, arg2, arg3})
if err != nil {
t.Fatal("expected no error; got", err)
}
+
+ if len(expected) != len(ss) {
+ t.Fatalf("expected number of ss to be %d but got: %d", len(expected), len(ss))
+ }
for i, v := range ss {
if expected[i] != v {
t.Fatalf("expected ss[%d] to be %s but got: %s", i, expected[i], v)
}
}
+
+ values, err := f.GetStringSlice("ss")
+ 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 ss[%d] to be %s but got: %s", i, expected[i], v)
+ }
+ }
}