Merge branch 'master' into master
This commit is contained in:
commit
b413f8dfb0
16
README.md
16
README.md
|
@ -148,6 +148,22 @@ fmt.Printf("Fetching the following IDs from %s: %q", args.Database, args.IDs)
|
|||
Fetching the following IDs from foo: [1 2 3]
|
||||
```
|
||||
|
||||
### Arguments that can be specified multiple times, mixed with positionals
|
||||
```go
|
||||
var args struct {
|
||||
Commands []string `arg:"-c,separate"`
|
||||
Files []string `arg:"-f,separate"`
|
||||
Databases []string `arg:"positional"`
|
||||
}
|
||||
```
|
||||
|
||||
```shell
|
||||
./example -c cmd1 db1 -f file1 db2 -c cmd2 -f file2 -f file3 db3 -c cmd3
|
||||
Commands: [cmd1 cmd2 cmd3]
|
||||
Files [file1 file2 file3]
|
||||
Databases [db1 db2 db3]
|
||||
```
|
||||
|
||||
### Custom validation
|
||||
```go
|
||||
var args struct {
|
||||
|
|
|
@ -71,6 +71,21 @@ func Example_multipleValues() {
|
|||
fmt.Printf("Fetching the following IDs from %s: %q", args.Database, args.IDs)
|
||||
}
|
||||
|
||||
// This eample demonstrates multiple value arguments that can be mixed with
|
||||
// other arguments.
|
||||
func Example_multipleMixed() {
|
||||
os.Args = []string{"./example", "-c", "cmd1", "db1", "-f", "file1", "db2", "-c", "cmd2", "-f", "file2", "-f", "file3", "db3", "-c", "cmd3"}
|
||||
var args struct {
|
||||
Commands []string `arg:"-c,separate"`
|
||||
Files []string `arg:"-f,separate"`
|
||||
Databases []string `arg:"positional"`
|
||||
}
|
||||
MustParse(&args)
|
||||
fmt.Println("Commands:", args.Commands)
|
||||
fmt.Println("Files", args.Files)
|
||||
fmt.Println("Databases", args.Databases)
|
||||
}
|
||||
|
||||
// This example shows the usage string generated by go-arg
|
||||
func Example_usageString() {
|
||||
// These are the args you would pass in on the command line
|
||||
|
|
14
parse.go
14
parse.go
|
@ -20,6 +20,7 @@ type spec struct {
|
|||
multiple bool
|
||||
required bool
|
||||
positional bool
|
||||
separate bool
|
||||
help string
|
||||
env string
|
||||
wasPresent bool
|
||||
|
@ -189,6 +190,8 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {
|
|||
spec.required = true
|
||||
case key == "positional":
|
||||
spec.positional = true
|
||||
case key == "separate":
|
||||
spec.separate = true
|
||||
case key == "help":
|
||||
spec.help = value
|
||||
case key == "env":
|
||||
|
@ -314,11 +317,14 @@ func process(specs []*spec, args []string) error {
|
|||
for i+1 < len(args) && !isFlag(args[i+1]) {
|
||||
values = append(values, args[i+1])
|
||||
i++
|
||||
if spec.separate {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
values = append(values, value)
|
||||
}
|
||||
err := setSlice(spec.dest, values)
|
||||
err := setSlice(spec.dest, values, !spec.separate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error processing %s: %v", arg, err)
|
||||
}
|
||||
|
@ -350,7 +356,7 @@ func process(specs []*spec, args []string) error {
|
|||
for _, spec := range specs {
|
||||
if spec.positional {
|
||||
if spec.multiple {
|
||||
err := setSlice(spec.dest, positionals)
|
||||
err := setSlice(spec.dest, positionals, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error processing %s: %v", spec.long, err)
|
||||
}
|
||||
|
@ -388,7 +394,7 @@ func validate(spec []*spec) error {
|
|||
}
|
||||
|
||||
// parse a value as the appropriate type and store it in the struct
|
||||
func setSlice(dest reflect.Value, values []string) error {
|
||||
func setSlice(dest reflect.Value, values []string, trunc bool) error {
|
||||
if !dest.CanSet() {
|
||||
return fmt.Errorf("field is not writable")
|
||||
}
|
||||
|
@ -401,7 +407,7 @@ func setSlice(dest reflect.Value, values []string) error {
|
|||
}
|
||||
|
||||
// Truncate the dest slice in case default values exist
|
||||
if !dest.IsNil() {
|
||||
if trunc && !dest.IsNil() {
|
||||
dest.SetLen(0)
|
||||
}
|
||||
|
||||
|
|
|
@ -739,3 +739,57 @@ func TestHyphenInMultiPositional(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"---", "x", "-", "y"}, args.Foo)
|
||||
}
|
||||
|
||||
func TestSeparate(t *testing.T) {
|
||||
for _, val := range []string{"-f one", "-f=one", "--foo one", "--foo=one"} {
|
||||
var args struct {
|
||||
Foo []string `arg:"--foo,-f,separate"`
|
||||
}
|
||||
|
||||
err := parse(val, &args)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"one"}, args.Foo)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeparateWithDefault(t *testing.T) {
|
||||
args := struct {
|
||||
Foo []string `arg:"--foo,-f,separate"`
|
||||
}{
|
||||
Foo: []string{"default"},
|
||||
}
|
||||
|
||||
err := parse("-f one -f=two", &args)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"default", "one", "two"}, args.Foo)
|
||||
}
|
||||
|
||||
func TestSeparateWithPositional(t *testing.T) {
|
||||
var args struct {
|
||||
Foo []string `arg:"--foo,-f,separate"`
|
||||
Bar string `arg:"positional"`
|
||||
Moo string `arg:"positional"`
|
||||
}
|
||||
|
||||
err := parse("zzz --foo one -f=two --foo=three -f four aaa", &args)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"one", "two", "three", "four"}, args.Foo)
|
||||
assert.Equal(t, "zzz", args.Bar)
|
||||
assert.Equal(t, "aaa", args.Moo)
|
||||
}
|
||||
|
||||
func TestSeparatePositionalInterweaved(t *testing.T) {
|
||||
var args struct {
|
||||
Foo []string `arg:"--foo,-f,separate"`
|
||||
Bar []string `arg:"--bar,-b,separate"`
|
||||
Pre string `arg:"positional"`
|
||||
Post []string `arg:"positional"`
|
||||
}
|
||||
|
||||
err := parse("zzz -f foo1 -b=bar1 --foo=foo2 -b bar2 post1 -b bar3 post2 post3", &args)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"foo1", "foo2"}, args.Foo)
|
||||
assert.Equal(t, []string{"bar1", "bar2", "bar3"}, args.Bar)
|
||||
assert.Equal(t, "zzz", args.Pre)
|
||||
assert.Equal(t, []string{"post1", "post2", "post3"}, args.Post)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue