Require cmd:"" or arg:"" for branching commands/args.
This commit is contained in:
@@ -10,7 +10,7 @@ import (
|
|||||||
func build(ast interface{}) (app *Application, err error) {
|
func build(ast interface{}) (app *Application, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
msg := recover()
|
msg := recover()
|
||||||
if test, ok := recover().(error); ok {
|
if test, ok := msg.(error); ok {
|
||||||
app = nil
|
app = nil
|
||||||
err = test
|
err = test
|
||||||
} else if msg != nil {
|
} else if msg != nil {
|
||||||
@@ -23,13 +23,16 @@ func build(ast interface{}) (app *Application, err error) {
|
|||||||
return nil, fmt.Errorf("expected a pointer to a struct but got %T", ast)
|
return nil, fmt.Errorf("expected a pointer to a struct but got %T", ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildNode(iv), nil
|
return buildNode(iv, true), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildNode(v reflect.Value) *Node {
|
func buildNode(v reflect.Value, cmd bool) *Node {
|
||||||
node := &Node{}
|
node := &Node{}
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
ft := v.Type().Field(i)
|
ft := v.Type().Field(i)
|
||||||
|
if strings.ToLower(ft.Name[0:1]) == ft.Name[0:1] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
fv := v.Field(i)
|
fv := v.Field(i)
|
||||||
|
|
||||||
name := ft.Tag.Get("name")
|
name := ft.Tag.Get("name")
|
||||||
@@ -37,10 +40,7 @@ func buildNode(v reflect.Value) *Node {
|
|||||||
name = strings.ToLower(strings.Join(camelCase(ft.Name), "-"))
|
name = strings.ToLower(strings.Join(camelCase(ft.Name), "-"))
|
||||||
}
|
}
|
||||||
decoder := DecoderForField(ft)
|
decoder := DecoderForField(ft)
|
||||||
help, ok := ft.Tag.Lookup("help")
|
help, _ := ft.Tag.Lookup("help")
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dflt := ft.Tag.Get("default")
|
dflt := ft.Tag.Get("default")
|
||||||
placeholder := ft.Tag.Get("placeholder")
|
placeholder := ft.Tag.Get("placeholder")
|
||||||
if placeholder == "" {
|
if placeholder == "" {
|
||||||
@@ -55,12 +55,15 @@ func buildNode(v reflect.Value) *Node {
|
|||||||
_, optional := ft.Tag.Lookup("optional")
|
_, optional := ft.Tag.Lookup("optional")
|
||||||
// Force field to be an argument, not a flag.
|
// Force field to be an argument, not a flag.
|
||||||
_, arg := ft.Tag.Lookup("arg")
|
_, arg := ft.Tag.Lookup("arg")
|
||||||
|
if !cmd {
|
||||||
|
_, cmd = ft.Tag.Lookup("cmd")
|
||||||
|
}
|
||||||
env := ft.Tag.Get("env")
|
env := ft.Tag.Get("env")
|
||||||
format := ft.Tag.Get("format")
|
format := ft.Tag.Get("format")
|
||||||
|
|
||||||
// Nested structs are either commands or args.
|
// Nested structs are either commands or args.
|
||||||
if ft.Type.Kind() == reflect.Struct && decoder == nil {
|
if ft.Type.Kind() == reflect.Struct && (cmd || arg) {
|
||||||
child := buildNode(fv)
|
child := buildNode(fv, false)
|
||||||
child.Help = help
|
child.Help = help
|
||||||
|
|
||||||
// A branching argument. This is a bit hairy, as we let buildNode() do the parsing, then check that
|
// A branching argument. This is a bit hairy, as we let buildNode() do the parsing, then check that
|
||||||
|
|||||||
+24
-23
@@ -17,11 +17,11 @@ func TestArgumentSequence(t *testing.T) {
|
|||||||
var cli struct {
|
var cli struct {
|
||||||
User struct {
|
User struct {
|
||||||
Create struct {
|
Create struct {
|
||||||
ID int `arg:"" help:""`
|
ID int `arg:""`
|
||||||
First string `arg:"" help:""`
|
First string `arg:""`
|
||||||
Last string `arg:"" help:""`
|
Last string `arg:""`
|
||||||
} `help:""`
|
} `cmd:""`
|
||||||
} `help:""`
|
} `cmd:""`
|
||||||
}
|
}
|
||||||
p := mustNew(t, &cli)
|
p := mustNew(t, &cli)
|
||||||
cmd, err := p.Parse([]string{"user", "create", "10", "Alec", "Thomas"})
|
cmd, err := p.Parse([]string{"user", "create", "10", "Alec", "Thomas"})
|
||||||
@@ -39,21 +39,21 @@ func TestBranchingArgument(t *testing.T) {
|
|||||||
var cli struct {
|
var cli struct {
|
||||||
User struct {
|
User struct {
|
||||||
Create struct {
|
Create struct {
|
||||||
ID string `arg:"" help:""`
|
ID string `arg:""`
|
||||||
First string `arg:"" help:""`
|
First string `arg:""`
|
||||||
Last string `arg:"" help:""`
|
Last string `arg:""`
|
||||||
} `help:""`
|
} `cmd:""`
|
||||||
|
|
||||||
// Branching argument.
|
// Branching argument.
|
||||||
ID struct {
|
ID struct {
|
||||||
ID int `arg:"" help:""`
|
ID int `arg:""`
|
||||||
Flag int `help:""`
|
Flag int
|
||||||
Delete struct{} `help:""`
|
Delete struct{} `cmd:""`
|
||||||
Rename struct {
|
Rename struct {
|
||||||
To string `help:""`
|
To string
|
||||||
} `help:""`
|
} `cmd:""`
|
||||||
} `arg:"" help:""`
|
} `arg:""`
|
||||||
} `help:"Manage users."`
|
} `cmd:"" help:"User management."`
|
||||||
}
|
}
|
||||||
p := mustNew(t, &cli)
|
p := mustNew(t, &cli)
|
||||||
cmd, err := p.Parse([]string{"user", "10", "delete"})
|
cmd, err := p.Parse([]string{"user", "10", "delete"})
|
||||||
@@ -64,8 +64,8 @@ func TestBranchingArgument(t *testing.T) {
|
|||||||
|
|
||||||
func TestResetWithDefaults(t *testing.T) {
|
func TestResetWithDefaults(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Flag string `help:""`
|
Flag string
|
||||||
FlagWithDefault string `default:"default" help:""`
|
FlagWithDefault string `default:"default" `
|
||||||
}
|
}
|
||||||
cli.Flag = "BLAH"
|
cli.Flag = "BLAH"
|
||||||
cli.FlagWithDefault = "BLAH"
|
cli.FlagWithDefault = "BLAH"
|
||||||
@@ -78,7 +78,7 @@ func TestResetWithDefaults(t *testing.T) {
|
|||||||
|
|
||||||
func TestFlagSlice(t *testing.T) {
|
func TestFlagSlice(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Slice []int `help:""`
|
Slice []int
|
||||||
}
|
}
|
||||||
parser := mustNew(t, &cli)
|
parser := mustNew(t, &cli)
|
||||||
_, err := parser.Parse([]string{"--slice=1,2,3"})
|
_, err := parser.Parse([]string{"--slice=1,2,3"})
|
||||||
@@ -88,8 +88,8 @@ func TestFlagSlice(t *testing.T) {
|
|||||||
|
|
||||||
func TestArgSlice(t *testing.T) {
|
func TestArgSlice(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Slice []int `help:"" arg:""`
|
Slice []int `arg:""`
|
||||||
Flag bool `help:""`
|
Flag bool
|
||||||
}
|
}
|
||||||
parser := mustNew(t, &cli)
|
parser := mustNew(t, &cli)
|
||||||
_, err := parser.Parse([]string{"1", "2", "3", "--flag"})
|
_, err := parser.Parse([]string{"1", "2", "3", "--flag"})
|
||||||
@@ -100,7 +100,8 @@ func TestArgSlice(t *testing.T) {
|
|||||||
|
|
||||||
func TestUnsupportedfieldErrors(t *testing.T) {
|
func TestUnsupportedfieldErrors(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Keys map[string]string `help:""`
|
Keys map[string]string
|
||||||
}
|
}
|
||||||
require.Panics(t, func() { mustNew(t, &cli) })
|
_, err := New("", "", &cli)
|
||||||
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Node struct {
|
|||||||
Children []*Branch
|
Children []*Branch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Value is either a flag or a variaable positional argument.
|
||||||
type Value struct {
|
type Value struct {
|
||||||
Flag bool // True if flag, false if positional argument.
|
Flag bool // True if flag, false if positional argument.
|
||||||
Name string
|
Name string
|
||||||
|
|||||||
Reference in New Issue
Block a user