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]
|
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
|
### Custom validation
|
||||||
```go
|
```go
|
||||||
var args struct {
|
var args struct {
|
||||||
|
|
|
@ -71,6 +71,21 @@ func Example_multipleValues() {
|
||||||
fmt.Printf("Fetching the following IDs from %s: %q", args.Database, args.IDs)
|
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
|
// This example shows the usage string generated by go-arg
|
||||||
func Example_usageString() {
|
func Example_usageString() {
|
||||||
// These are the args you would pass in on the command line
|
// 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
|
multiple bool
|
||||||
required bool
|
required bool
|
||||||
positional bool
|
positional bool
|
||||||
|
separate bool
|
||||||
help string
|
help string
|
||||||
env string
|
env string
|
||||||
wasPresent bool
|
wasPresent bool
|
||||||
|
@ -189,6 +190,8 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {
|
||||||
spec.required = true
|
spec.required = true
|
||||||
case key == "positional":
|
case key == "positional":
|
||||||
spec.positional = true
|
spec.positional = true
|
||||||
|
case key == "separate":
|
||||||
|
spec.separate = true
|
||||||
case key == "help":
|
case key == "help":
|
||||||
spec.help = value
|
spec.help = value
|
||||||
case key == "env":
|
case key == "env":
|
||||||
|
@ -314,11 +317,14 @@ func process(specs []*spec, args []string) error {
|
||||||
for i+1 < len(args) && !isFlag(args[i+1]) {
|
for i+1 < len(args) && !isFlag(args[i+1]) {
|
||||||
values = append(values, args[i+1])
|
values = append(values, args[i+1])
|
||||||
i++
|
i++
|
||||||
|
if spec.separate {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
}
|
}
|
||||||
err := setSlice(spec.dest, values)
|
err := setSlice(spec.dest, values, !spec.separate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error processing %s: %v", arg, err)
|
return fmt.Errorf("error processing %s: %v", arg, err)
|
||||||
}
|
}
|
||||||
|
@ -350,7 +356,7 @@ func process(specs []*spec, args []string) error {
|
||||||
for _, spec := range specs {
|
for _, spec := range specs {
|
||||||
if spec.positional {
|
if spec.positional {
|
||||||
if spec.multiple {
|
if spec.multiple {
|
||||||
err := setSlice(spec.dest, positionals)
|
err := setSlice(spec.dest, positionals, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error processing %s: %v", spec.long, err)
|
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
|
// 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() {
|
if !dest.CanSet() {
|
||||||
return fmt.Errorf("field is not writable")
|
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
|
// Truncate the dest slice in case default values exist
|
||||||
if !dest.IsNil() {
|
if trunc && !dest.IsNil() {
|
||||||
dest.SetLen(0)
|
dest.SetLen(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -739,3 +739,57 @@ func TestHyphenInMultiPositional(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []string{"---", "x", "-", "y"}, args.Foo)
|
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