From fa12c02e81bdff1e9c11ee8a70b938b8c02da6b6 Mon Sep 17 00:00:00 2001 From: Alex Flint Date: Mon, 24 May 2021 21:45:11 -0700 Subject: [PATCH 1/4] recurse into unexported embedded structs --- parse.go | 13 ++++++++++--- parse_test.go | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/parse.go b/parse.go index 13e8195..d76ef0f 100644 --- a/parse.go +++ b/parse.go @@ -257,17 +257,24 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) { var errs []string walkFields(t, func(field reflect.StructField, t reflect.Type) bool { - // Check for the ignore switch in the tag + // check for the ignore switch in the tag tag := field.Tag.Get("arg") - if tag == "-" || !isExported(field.Name) { + if tag == "-" { return false } - // If this is an embedded struct then recurse into its fields + // if this is an embedded struct then recurse into its fields, even if + // it is unexported, because exported fields on unexported embedded + // structs are still writable if field.Anonymous && field.Type.Kind() == reflect.Struct { return true } + // ignore any other unexported field + if !isExported(field.Name) { + return false + } + // duplicate the entire path to avoid slice overwrites subdest := dest.Child(field) spec := spec{ diff --git a/parse_test.go b/parse_test.go index 695b1d8..e78b4d4 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1095,6 +1095,29 @@ func TestEmbeddedWithDuplicateField2(t *testing.T) { assert.Equal(t, "", args.U.A) } +func TestUnexportedEmbedded(t *testing.T) { + type embeddedArgs struct { + Foo string + } + var args struct { + embeddedArgs + } + err := parse("--foo bar", &args) + require.NoError(t, err) + assert.Equal(t, "bar", args.Foo) +} + +func TestIgnoredEmbedded(t *testing.T) { + type embeddedArgs struct { + Foo string + } + var args struct { + embeddedArgs `arg:"-"` + } + err := parse("--foo bar", &args) + require.Error(t, err) +} + func TestEmptyArgs(t *testing.T) { origArgs := os.Args From 3d59e5e89e775b1d33a451aca6aa4bda4fbad500 Mon Sep 17 00:00:00 2001 From: Alex Flint Date: Fri, 20 Aug 2021 19:52:48 -0700 Subject: [PATCH 2/4] bump go-scalar to v1.1 and add documentation about supported types --- README.md | 16 ++++++++++++++++ example_test.go | 35 +++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 2 ++ parse_test.go | 19 +++++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 48fa2f0..229a9df 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,22 @@ func main() { As usual, any field tagged with `arg:"-"` is ignored. +### Supported types + +The following types may be used as arguments: +- built-in integer types: `int, int8, int16, int32, int64, byte, rune` +- built-in floating point types: `float32, float64` +- strings +- booleans +- URLs represented as `url.URL` +- time durations represented as `time.Duration` +- email addresses represented as `mail.Address` +- MAC addresses represented as `net.HardwareAddr` +- pointers to any of the above +- slices of any of the above +- maps using any of the above as keys and values +- any type that implements `encoding.TextUnmarshaler` + ### Custom parsing Implement `encoding.TextUnmarshaler` to define your own parsing logic. diff --git a/example_test.go b/example_test.go index 2e9b875..8394289 100644 --- a/example_test.go +++ b/example_test.go @@ -2,8 +2,12 @@ package arg import ( "fmt" + "net" + "net/mail" + "net/url" "os" "strings" + "time" ) func split(s string) []string { @@ -377,3 +381,34 @@ func Example_subcommand() { // output: // commit requested with message "what-this-commit-is-about" } + +func Example_allSupportedTypes() { + // These are the args you would pass in on the command line + os.Args = []string{} + + var args struct { + Bool bool + Byte byte + Rune rune + Int int + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + Float32 float32 + Float64 float64 + String string + Duration time.Duration + URL url.URL + Email mail.Address + MAC net.HardwareAddr + } + + // go-arg supports each of the types above, as well as pointers to any of + // the above and slices of any of the above. It also supports any types that + // implements encoding.TextUnmarshaler. + + MustParse(&args) + + // output: +} diff --git a/go.mod b/go.mod index 14c6119..8fb1508 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/alexflint/go-arg require ( - github.com/alexflint/go-scalar v1.0.0 + github.com/alexflint/go-scalar v1.1.0 github.com/stretchr/testify v1.2.2 ) diff --git a/go.sum b/go.sum index cebe9a2..4cc35da 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70= github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= +github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM= +github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/parse_test.go b/parse_test.go index e78b4d4..284e386 100644 --- a/parse_test.go +++ b/parse_test.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/mail" + "net/url" "os" "strings" "testing" @@ -961,6 +962,24 @@ func TestPtrToIP(t *testing.T) { assert.Equal(t, "192.168.0.1", args.Host.String()) } +func TestURL(t *testing.T) { + var args struct { + URL url.URL + } + err := parse("--url https://example.com/get?item=xyz", &args) + require.NoError(t, err) + assert.Equal(t, "https://example.com/get?item=xyz", args.URL.String()) +} + +func TestPtrToURL(t *testing.T) { + var args struct { + URL *url.URL + } + err := parse("--url http://example.com/#xyz", &args) + require.NoError(t, err) + assert.Equal(t, "http://example.com/#xyz", args.URL.String()) +} + func TestIPSlice(t *testing.T) { var args struct { Host []net.IP From b157e8d10a29fcea34b33988776848a322ef69dc Mon Sep 17 00:00:00 2001 From: evgenv123 <83824538+evgenv123@users.noreply.github.com> Date: Sat, 18 Sep 2021 22:23:26 +0700 Subject: [PATCH 3/4] Update README.md Hi! As a first-time user of your great package I got a little bit confused on using command line args and env vars together, so it took me some time to make testing and I propose to save this time for other people by adding relevant edits to README.md --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 229a9df..855c02e 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,19 @@ var args struct { arg.MustParse(&args) ``` +### Priority + +You can use both command line arguments and environmental variables at the same time. +The priority is as follows: command line arguments -> if empty we check environmental variables -> and then we use default values + +```go +var args struct { + Command string `arg:"-c,env:COMMAND" help:"Command to execute" default:"remove"` + File string `arg:"-f,env:FILE_NAME"` +} +arg.MustParse(&args) +``` + ### Default values (before v1.2) ```go From 0f0b4b5c3f9dfab413c3127830b869bc7d4b0e27 Mon Sep 17 00:00:00 2001 From: Alex Flint Date: Sat, 18 Sep 2021 08:50:33 -0700 Subject: [PATCH 4/4] Update README.md --- README.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 855c02e..dab2996 100644 --- a/README.md +++ b/README.md @@ -158,19 +158,6 @@ var args struct { arg.MustParse(&args) ``` -### Priority - -You can use both command line arguments and environmental variables at the same time. -The priority is as follows: command line arguments -> if empty we check environmental variables -> and then we use default values - -```go -var args struct { - Command string `arg:"-c,env:COMMAND" help:"Command to execute" default:"remove"` - File string `arg:"-f,env:FILE_NAME"` -} -arg.MustParse(&args) -``` - ### Default values (before v1.2) ```go @@ -182,6 +169,17 @@ arg.Foo = "abc" arg.MustParse(&args) ``` +### Combining command line options, environment variables, and default values + +You can combine command line arguments, environment variables, and default values. Command line arguments take precedence over environment variables, which take precedence over default values. This means that we check whether a certain option was provided on the command line, then if not, we check for an environment variable (only if an `env` tag was provided), then if none is found, we check for a `default` tag containing a default value. + +```go +var args struct { + Test string `arg:"-t,env:TEST" default:"something"` +} +arg.MustParse(&args) +``` + ### Arguments with multiple values ```go var args struct {