Don't allow non flag arguments by default.
This commit is contained in:
parent
be888b59a6
commit
31614bebc4
2 changed files with 68 additions and 4 deletions
|
@ -3,7 +3,6 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -17,7 +16,9 @@ type Command struct {
|
||||||
Resources []ResourceLoader
|
Resources []ResourceLoader
|
||||||
Run func([]string) error
|
Run func([]string) error
|
||||||
Hidden bool
|
Hidden bool
|
||||||
subCommands []*Command
|
// AllowArg if not set, disallows any argument that is not a known command or a sub-command.
|
||||||
|
AllowArg bool
|
||||||
|
subCommands []*Command
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddCommand Adds a sub command.
|
// AddCommand Adds a sub command.
|
||||||
|
@ -40,13 +41,17 @@ func Execute(cmd *Command) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func execute(cmd *Command, args []string, root bool) error {
|
func execute(cmd *Command, args []string, root bool) error {
|
||||||
|
// Calls command without args.
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
if err := run(cmd, args); err != nil {
|
if err := run(cmd, args[1:]); err != nil {
|
||||||
return fmt.Errorf("command %s error: %v", args[0], err)
|
return fmt.Errorf("command %s error: %v", args[0], err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special case: if the command is the top level one,
|
||||||
|
// and the first arg (`args[1]`) is not the command name or a known sub-command,
|
||||||
|
// then we run the top level command itself.
|
||||||
if root && cmd.Name != args[1] && !contains(cmd.subCommands, args[1]) {
|
if root && cmd.Name != args[1] && !contains(cmd.subCommands, args[1]) {
|
||||||
if err := run(cmd, args[1:]); err != nil {
|
if err := run(cmd, args[1:]); err != nil {
|
||||||
return fmt.Errorf("command %s error: %v", filepath.Base(args[0]), err)
|
return fmt.Errorf("command %s error: %v", filepath.Base(args[0]), err)
|
||||||
|
@ -54,6 +59,7 @@ func execute(cmd *Command, args []string, root bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calls command by its name.
|
||||||
if len(args) >= 2 && cmd.Name == args[1] {
|
if len(args) >= 2 && cmd.Name == args[1] {
|
||||||
if err := run(cmd, args[2:]); err != nil {
|
if err := run(cmd, args[2:]); err != nil {
|
||||||
return fmt.Errorf("command %s error: %v", cmd.Name, err)
|
return fmt.Errorf("command %s error: %v", cmd.Name, err)
|
||||||
|
@ -61,6 +67,7 @@ func execute(cmd *Command, args []string, root bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No sub-command, calls the current command.
|
||||||
if len(cmd.subCommands) == 0 {
|
if len(cmd.subCommands) == 0 {
|
||||||
if err := run(cmd, args[1:]); err != nil {
|
if err := run(cmd, args[1:]); err != nil {
|
||||||
return fmt.Errorf("command %s error: %v", cmd.Name, err)
|
return fmt.Errorf("command %s error: %v", cmd.Name, err)
|
||||||
|
@ -68,6 +75,7 @@ func execute(cmd *Command, args []string, root bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trying to find the sub-command.
|
||||||
for _, subCmd := range cmd.subCommands {
|
for _, subCmd := range cmd.subCommands {
|
||||||
if len(args) >= 2 && subCmd.Name == args[1] {
|
if len(args) >= 2 && subCmd.Name == args[1] {
|
||||||
return execute(subCmd, args[1:], false)
|
return execute(subCmd, args[1:], false)
|
||||||
|
@ -84,7 +92,12 @@ func run(cmd *Command, args []string) error {
|
||||||
|
|
||||||
if cmd.Run == nil {
|
if cmd.Run == nil {
|
||||||
_ = PrintHelp(os.Stdout, cmd)
|
_ = PrintHelp(os.Stdout, cmd)
|
||||||
return errors.New("command not found")
|
return fmt.Errorf("command %s is not runnable", cmd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 0 && !isFlag(args[0]) && !cmd.AllowArg {
|
||||||
|
_ = PrintHelp(os.Stdout, cmd)
|
||||||
|
return fmt.Errorf("command not found: %v", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.Configuration == nil {
|
if cmd.Configuration == nil {
|
||||||
|
@ -113,3 +126,7 @@ func contains(cmds []*Command, name string) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isFlag(arg string) bool {
|
||||||
|
return len(arg) > 0 && arg[1] == '-'
|
||||||
|
}
|
||||||
|
|
|
@ -86,6 +86,23 @@ func Test_execute(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: expected{result: "root"},
|
expected: expected{result: "root"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "root command, with argument, command not found",
|
||||||
|
args: []string{"", "echo"},
|
||||||
|
command: func() *Command {
|
||||||
|
return &Command{
|
||||||
|
Name: "root",
|
||||||
|
Description: "This is a test",
|
||||||
|
Configuration: nil,
|
||||||
|
Run: func(_ []string) error {
|
||||||
|
called = "root"
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
expected: expected{error: true},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "one sub command",
|
desc: "one sub command",
|
||||||
args: []string{"", "sub1"},
|
args: []string{"", "sub1"},
|
||||||
|
@ -114,6 +131,34 @@ func Test_execute(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: expected{result: "sub1"},
|
expected: expected{result: "sub1"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "one sub command, with argument, command not found",
|
||||||
|
args: []string{"", "sub1", "echo"},
|
||||||
|
command: func() *Command {
|
||||||
|
rootCmd := &Command{
|
||||||
|
Name: "test",
|
||||||
|
Description: "This is a test",
|
||||||
|
Configuration: nil,
|
||||||
|
Run: func(_ []string) error {
|
||||||
|
called += "root"
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = rootCmd.AddCommand(&Command{
|
||||||
|
Name: "sub1",
|
||||||
|
Description: "sub1",
|
||||||
|
Configuration: nil,
|
||||||
|
Run: func(_ []string) error {
|
||||||
|
called += "sub1"
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return rootCmd
|
||||||
|
},
|
||||||
|
expected: expected{error: true},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "two sub commands",
|
desc: "two sub commands",
|
||||||
args: []string{"", "sub2"},
|
args: []string{"", "sub2"},
|
||||||
|
@ -376,6 +421,7 @@ func Test_execute(t *testing.T) {
|
||||||
Name: "sub1",
|
Name: "sub1",
|
||||||
Description: "sub1",
|
Description: "sub1",
|
||||||
Configuration: nil,
|
Configuration: nil,
|
||||||
|
AllowArg: true,
|
||||||
Run: func(args []string) error {
|
Run: func(args []string) error {
|
||||||
called += "sub1-" + strings.Join(args, "-")
|
called += "sub1-" + strings.Join(args, "-")
|
||||||
return nil
|
return nil
|
||||||
|
@ -394,6 +440,7 @@ func Test_execute(t *testing.T) {
|
||||||
Name: "root",
|
Name: "root",
|
||||||
Description: "This is a test",
|
Description: "This is a test",
|
||||||
Configuration: nil,
|
Configuration: nil,
|
||||||
|
AllowArg: true,
|
||||||
Run: func(args []string) error {
|
Run: func(args []string) error {
|
||||||
called += "root-" + strings.Join(args, "-")
|
called += "root-" + strings.Join(args, "-")
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in a new issue