diff --git a/README.md b/README.md index 81b9505..fa90ad5 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,8 @@ type CLI struct { } ``` +If a sub-command is tagged with `default:"1"` it will be selected if there are no further arguments. + ## Branching positional arguments @@ -419,6 +421,7 @@ Tag | Description `type:"X"` | Specify [named types](#custom-named-types) to use. `placeholder:"X"` | Placeholder text. `default:"X"` | Default value. +`default:"1"` | On a command, make it the default. `short:"X"` | Short name, if flag. `required` | If present, flag/arg is required. `optional` | If present, flag/arg is optional. diff --git a/context.go b/context.go index 9baca0e..86e6f5b 100644 --- a/context.go +++ b/context.go @@ -397,6 +397,24 @@ func (c *Context) trace(node *Node) (err error) { // nolint: gocyclo return fmt.Errorf("unexpected token %s", token) } } + + // End of the line, check for a default command. + var defaultNode *Path + for _, child := range node.Children { + if child.Type == CommandNode && child.Tag.Default != "" { + if defaultNode != nil { + return fmt.Errorf("can't have more than one default command under %s", node.Summary()) + } + defaultNode = &Path{ + Parent: child, + Command: child, + Flags: child.Flags, + } + } + } + if defaultNode != nil { + c.Path = append(c.Path, defaultNode) + } return nil } @@ -617,6 +635,7 @@ func checkMissingChildren(node *Node) error { missing = append(missing, strconv.Quote(strings.Join(missingArgs, " "))) } + haveDefault := 0 for _, child := range node.Children { if child.Hidden { continue @@ -627,10 +646,19 @@ func checkMissingChildren(node *Node) error { } missing = append(missing, strconv.Quote(child.Summary())) } else { + if child.Tag.Default != "" { + if len(child.Children) > 0 { + return fmt.Errorf("default command %s must not have subcommands or arguments", child.Summary()) + } + haveDefault++ + } missing = append(missing, strconv.Quote(child.Name)) } } - if len(missing) == 0 { + if haveDefault > 1 { + return fmt.Errorf("more than one default command found under %s", node.Summary()) + } + if len(missing) == 0 || haveDefault > 0 { return nil } diff --git a/kong_test.go b/kong_test.go index 75a7fcd..b55093a 100644 --- a/kong_test.go +++ b/kong_test.go @@ -815,3 +815,38 @@ func TestIssue40EnumAcrossCommands(t *testing.T) { _, err = p.Parse([]string{"three", "c"}) require.NoError(t, err) } + +func TestEnumArg(t *testing.T) { + var cli struct { + Nested struct { + One string `arg:"" enum:"a,b,c"` + Two string `arg:""` + } `cmd:""` + } + p := mustNew(t, &cli) + _, err := p.Parse([]string{"nested", "a", "b"}) + require.NoError(t, err) + require.Equal(t, "a", cli.Nested.One) + require.Equal(t, "b", cli.Nested.Two) +} + +func TestDefaultCommand(t *testing.T) { + var cli struct { + One struct{} `cmd:"" default:"1"` + Two struct{} `cmd:""` + } + p := mustNew(t, &cli) + ctx, err := p.Parse([]string{}) + require.NoError(t, err) + require.Equal(t, "one", ctx.Command()) +} + +func TestMultipleDefaultCommands(t *testing.T) { + var cli struct { + One struct{} `cmd:"" default:"1"` + Two struct{} `cmd:"" default:"1"` + } + p := mustNew(t, &cli) + _, err := p.Parse([]string{}) + require.EqualError(t, err, "can't have more than one default command under ") +}