Merge remote-tracking branch 'origin/master' into usage-for-subcommands
This commit is contained in:
commit
f2f876420c
27
README.md
27
README.md
|
@ -169,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 {
|
||||
|
@ -308,6 +319,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.
|
||||
|
|
|
@ -2,8 +2,12 @@ package arg
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func split(s string) []string {
|
||||
|
@ -470,3 +474,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:
|
||||
}
|
||||
|
|
2
go.mod
2
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
|
||||
)
|
||||
|
||||
|
|
2
go.sum
2
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=
|
||||
|
|
13
parse.go
13
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{
|
||||
|
|
|
@ -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
|
||||
|
@ -1095,6 +1114,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
|
||||
|
||||
|
|
Loading…
Reference in New Issue