Skip to content

Commit c1e2c64

Browse files
committed
args: add new UnknownCommandError type
This commit makes unknown commands a proper error type so that the caller can check for this specific error type without having to do string comparisons. This is useful in the context of #823
1 parent 756ba6d commit c1e2c64

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

args.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ import (
2121

2222
type PositionalArgs func(cmd *Command, args []string) error
2323

24+
// UnknownCommandError is returned for unknown command
25+
type UnknownCommandError struct {
26+
unknownCmd string
27+
cmdPath string
28+
suggestions string
29+
}
30+
31+
func (e UnknownCommandError) Error() string {
32+
return fmt.Sprintf("unknown command %q for %q%s", e.unknownCmd, e.cmdPath, e.suggestions)
33+
}
34+
2435
// legacyArgs validation has the following behaviour:
2536
// - root commands with no subcommands can take arbitrary arguments
2637
// - root commands with subcommands will do subcommand validity checking
@@ -33,15 +44,15 @@ func legacyArgs(cmd *Command, args []string) error {
3344

3445
// root command with subcommands, do subcommand checking.
3546
if !cmd.HasParent() && len(args) > 0 {
36-
return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
47+
return UnknownCommandError{args[0], cmd.CommandPath(), cmd.findSuggestions(args[0])}
3748
}
3849
return nil
3950
}
4051

4152
// NoArgs returns an error if any args are included.
4253
func NoArgs(cmd *Command, args []string) error {
4354
if len(args) > 0 {
44-
return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath())
55+
return UnknownCommandError{args[0], cmd.CommandPath(), ""}
4556
}
4657
return nil
4758
}

command_test.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,10 @@ func TestRootExecuteUnknownCommand(t *testing.T) {
147147
rootCmd := &Command{Use: "root", Run: emptyRun}
148148
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
149149

150-
output, _ := executeCommand(rootCmd, "unknown")
150+
output, err := executeCommand(rootCmd, "unknown")
151+
if _, ok := err.(UnknownCommandError); !ok {
152+
t.Errorf("Expected:\n %T\nGot:\n %T\n", err, UnknownCommandError{})
153+
}
151154

152155
expected := "Error: unknown command \"unknown\" for \"root\"\nRun 'root --help' for usage.\n"
153156

@@ -156,6 +159,16 @@ func TestRootExecuteUnknownCommand(t *testing.T) {
156159
}
157160
}
158161

162+
func TestRootFindUnknownCommandErrorType(t *testing.T) {
163+
rootCmd := &Command{Use: "root", Run: emptyRun}
164+
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
165+
166+
_, _, err := rootCmd.Find([]string{"unknown"})
167+
if _, ok := err.(UnknownCommandError); !ok {
168+
t.Errorf("Expected:\n %T\nGot:\n %T\n", err, UnknownCommandError{})
169+
}
170+
}
171+
159172
func TestSubcommandExecuteC(t *testing.T) {
160173
rootCmd := &Command{Use: "root", Run: emptyRun}
161174
childCmd := &Command{Use: "child", Run: emptyRun}

0 commit comments

Comments
 (0)