From 5225bc2f3cdb8a14f4eb49fdf46170da10ba49fc Mon Sep 17 00:00:00 2001 From: Pavel Borzenkov Date: Tue, 20 Nov 2018 12:18:45 +0300 Subject: [PATCH 1/4] vendor: update go-scalar to the latest version Allows to use values (not pointer) with custom TextUnmarshaler. Signed-off-by: Pavel Borzenkov --- Godeps/Godeps.json | 4 ++-- .../github.com/alexflint/go-scalar/scalar.go | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index e8e7f3a..a6cfe20 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,14 +1,14 @@ { "ImportPath": "github.com/alexflint/go-arg", "GoVersion": "go1.7", - "GodepVersion": "v79", + "GodepVersion": "v80", "Packages": [ "." ], "Deps": [ { "ImportPath": "github.com/alexflint/go-scalar", - "Rev": "45e5d6cd8605faef82fda2bacc59e734a0b6f1f0" + "Rev": "6ab8ad5e1c5b25ca2783fe83f493c3ab471407e2" }, { "ImportPath": "github.com/stretchr/testify/assert", diff --git a/vendor/github.com/alexflint/go-scalar/scalar.go b/vendor/github.com/alexflint/go-scalar/scalar.go index 663f143..073392c 100644 --- a/vendor/github.com/alexflint/go-scalar/scalar.go +++ b/vendor/github.com/alexflint/go-scalar/scalar.go @@ -18,7 +18,6 @@ var ( textUnmarshalerType = reflect.TypeOf([]encoding.TextUnmarshaler{}).Elem() durationType = reflect.TypeOf(time.Duration(0)) mailAddressType = reflect.TypeOf(mail.Address{}) - ipType = reflect.TypeOf(net.IP{}) macType = reflect.TypeOf(net.HardwareAddr{}) ) @@ -47,6 +46,13 @@ func ParseValue(v reflect.Value, s string) error { if scalar, ok := v.Interface().(encoding.TextUnmarshaler); ok { return scalar.UnmarshalText([]byte(s)) } + // If it's a value instead of a pointer, check that we can unmarshal it + // via TextUnmarshaler as well + if v.CanAddr() { + if scalar, ok := v.Addr().Interface().(encoding.TextUnmarshaler); ok { + return scalar.UnmarshalText([]byte(s)) + } + } // If we have a pointer then dereference it if v.Kind() == reflect.Ptr { @@ -73,13 +79,6 @@ func ParseValue(v reflect.Value, s string) error { } v.Set(reflect.ValueOf(*addr)) return nil - case net.IP: - ip := net.ParseIP(s) - if ip == nil { - return fmt.Errorf(`invalid IP address: "%s"`, s) - } - v.Set(reflect.ValueOf(ip)) - return nil case net.HardwareAddr: ip, err := net.ParseMAC(s) if err != nil { @@ -126,7 +125,7 @@ func ParseValue(v reflect.Value, s string) error { // CanParse returns true if the type can be parsed from a string. func CanParse(t reflect.Type) bool { // If it implements encoding.TextUnmarshaler then use that - if t.Implements(textUnmarshalerType) { + if t.Implements(textUnmarshalerType) || reflect.PtrTo(t).Implements(textUnmarshalerType) { return true } @@ -137,7 +136,7 @@ func CanParse(t reflect.Type) bool { // Check for other special types switch t { - case durationType, mailAddressType, ipType, macType: + case durationType, mailAddressType, macType: return true } From 96b097bef382f73bdd47c1dd30ddaf9fb130aaf5 Mon Sep 17 00:00:00 2001 From: Pavel Borzenkov Date: Tue, 20 Nov 2018 12:29:15 +0300 Subject: [PATCH 2/4] parse_test: fix formatting Signed-off-by: Pavel Borzenkov --- parse_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/parse_test.go b/parse_test.go index 0bc97e3..d201b2f 100644 --- a/parse_test.go +++ b/parse_test.go @@ -580,7 +580,7 @@ func TestEnvironmentVariableRequired(t *testing.T) { assert.Equal(t, "bar", args.Foo) } -func TestEnvironmentVariableSliceArgumentString(t *testing.T) { +func TestEnvironmentVariableSliceArgumentString(t *testing.T) { var args struct { Foo []string `arg:"env"` } @@ -589,7 +589,7 @@ func TestEnvironmentVariableSliceArgumentString(t *testing.T) { assert.Equal(t, []string{"bar", "baz, qux"}, args.Foo) } -func TestEnvironmentVariableSliceArgumentInteger(t *testing.T) { +func TestEnvironmentVariableSliceArgumentInteger(t *testing.T) { var args struct { Foo []int `arg:"env"` } @@ -598,7 +598,7 @@ func TestEnvironmentVariableSliceArgumentInteger(t *testing.T) { assert.Equal(t, []int{1, 99}, args.Foo) } -func TestEnvironmentVariableSliceArgumentFloat(t *testing.T) { +func TestEnvironmentVariableSliceArgumentFloat(t *testing.T) { var args struct { Foo []float32 `arg:"env"` } @@ -607,7 +607,7 @@ func TestEnvironmentVariableSliceArgumentFloat(t *testing.T) { assert.Equal(t, []float32{1.1, 99.9}, args.Foo) } -func TestEnvironmentVariableSliceArgumentBool(t *testing.T) { +func TestEnvironmentVariableSliceArgumentBool(t *testing.T) { var args struct { Foo []bool `arg:"env"` } @@ -616,7 +616,7 @@ func TestEnvironmentVariableSliceArgumentBool(t *testing.T) { assert.Equal(t, []bool{true, false, false, true}, args.Foo) } -func TestEnvironmentVariableSliceArgumentWrongCsv(t *testing.T) { +func TestEnvironmentVariableSliceArgumentWrongCsv(t *testing.T) { var args struct { Foo []int `arg:"env"` } @@ -625,7 +625,7 @@ func TestEnvironmentVariableSliceArgumentWrongCsv(t *testing.T) { assert.Error(t, err) } -func TestEnvironmentVariableSliceArgumentWrongType(t *testing.T) { +func TestEnvironmentVariableSliceArgumentWrongType(t *testing.T) { var args struct { Foo []bool `arg:"env"` } From f1aabd5026533b5f61cabc38a5fd8a1af3148342 Mon Sep 17 00:00:00 2001 From: Pavel Borzenkov Date: Tue, 20 Nov 2018 12:29:36 +0300 Subject: [PATCH 3/4] parse_test: add tests covering new TextUnamarshaler value support Signed-off-by: Pavel Borzenkov --- parse_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/parse_test.go b/parse_test.go index d201b2f..b72563c 100644 --- a/parse_test.go +++ b/parse_test.go @@ -644,6 +644,16 @@ func (f *textUnmarshaler) UnmarshalText(b []byte) error { } func TestTextUnmarshaler(t *testing.T) { + // fields that implement TextUnmarshaler should be parsed using that interface + var args struct { + Foo textUnmarshaler + } + err := parse("--foo abc", &args) + require.NoError(t, err) + assert.Equal(t, 3, args.Foo.val) +} + +func TestPtrToTextUnmarshaler(t *testing.T) { // fields that implement TextUnmarshaler should be parsed using that interface var args struct { Foo *textUnmarshaler @@ -654,6 +664,19 @@ func TestTextUnmarshaler(t *testing.T) { } func TestRepeatedTextUnmarshaler(t *testing.T) { + // fields that implement TextUnmarshaler should be parsed using that interface + var args struct { + Foo []textUnmarshaler + } + err := parse("--foo abc d ef", &args) + require.NoError(t, err) + require.Len(t, args.Foo, 3) + assert.Equal(t, 3, args.Foo[0].val) + assert.Equal(t, 1, args.Foo[1].val) + assert.Equal(t, 2, args.Foo[2].val) +} + +func TestRepeatedPtrToTextUnmarshaler(t *testing.T) { // fields that implement TextUnmarshaler should be parsed using that interface var args struct { Foo []*textUnmarshaler @@ -667,6 +690,19 @@ func TestRepeatedTextUnmarshaler(t *testing.T) { } func TestPositionalTextUnmarshaler(t *testing.T) { + // fields that implement TextUnmarshaler should be parsed using that interface + var args struct { + Foo []textUnmarshaler `arg:"positional"` + } + err := parse("abc d ef", &args) + require.NoError(t, err) + require.Len(t, args.Foo, 3) + assert.Equal(t, 3, args.Foo[0].val) + assert.Equal(t, 1, args.Foo[1].val) + assert.Equal(t, 2, args.Foo[2].val) +} + +func TestPositionalPtrToTextUnmarshaler(t *testing.T) { // fields that implement TextUnmarshaler should be parsed using that interface var args struct { Foo []*textUnmarshaler `arg:"positional"` From a6af419fff15e031332604d9d99a9318881e99a7 Mon Sep 17 00:00:00 2001 From: Pavel Borzenkov Date: Tue, 20 Nov 2018 12:32:32 +0300 Subject: [PATCH 4/4] README: update TextUnmarshaler example Values are much more convenient to use in argument structs, so update README to use them instead of pointers in the example as we now support this. Signed-off-by: Pavel Borzenkov --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8980ba1..8e68ca7 100644 --- a/README.md +++ b/README.md @@ -288,10 +288,10 @@ func (n *NameDotName) MarshalText() (text []byte, err error) { func main() { var args struct { - Name *NameDotName + Name NameDotName } // set default - args.Name = &NameDotName{"file", "txt"} + args.Name = NameDotName{"file", "txt"} arg.MustParse(&args) fmt.Printf("%#v\n", args.Name) } @@ -305,10 +305,10 @@ Options: --help, -h display this help and exit $ ./example -&main.NameDotName{Head:"file", Tail:"txt"} +main.NameDotName{Head:"file", Tail:"txt"} $ ./example --name=foo.bar -&main.NameDotName{Head:"foo", Tail:"bar"} +main.NameDotName{Head:"foo", Tail:"bar"} $ ./example --name=oops Usage: example [--name NAME]