Skip to content

Commit eaafbde

Browse files
committed
feat(cli): add typed error handling, remove IsCommandErr
Add error types for CLI commands: - ErrSilent: command already printed error, exit 1 silently - ErrCancel: user cancelled (ctrl-c), exit 0 - FlagError: bad flags/args, print error + usage - HandleError(err): helper that handles all types Remove commander.IsCommandErr which used fragile string matching. Usage: if err := rootCmd.Execute(); err != nil { cli.HandleError(err) }
1 parent 77c342a commit eaafbde

File tree

2 files changed

+51
-29
lines changed

2 files changed

+51
-29
lines changed

cli/commander/manager.go

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package commander
22

3-
import (
4-
"strings"
5-
6-
"github.com/spf13/cobra"
7-
)
3+
import "github.com/spf13/cobra"
84

95
// Manager manages and configures features for a CLI tool.
106
type Manager struct {
@@ -106,27 +102,3 @@ func WithHooks(hooks []HookBehavior) func(*Manager) {
106102
m.Hooks = hooks
107103
}
108104
}
109-
110-
// IsCommandErr checks if the given error is related to a Cobra command error.
111-
// This is useful for distinguishing between user errors (e.g., incorrect commands or flags)
112-
// and program errors, allowing the application to display appropriate messages.
113-
func IsCommandErr(err error) bool {
114-
if err == nil {
115-
return false
116-
}
117-
118-
// Known Cobra command error keywords
119-
cmdErrorKeywords := []string{
120-
"unknown command",
121-
"unknown flag",
122-
"unknown shorthand flag",
123-
}
124-
125-
errMessage := err.Error()
126-
for _, keyword := range cmdErrorKeywords {
127-
if strings.Contains(errMessage, keyword) {
128-
return true
129-
}
130-
}
131-
return false
132-
}

cli/error.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package cli
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
)
8+
9+
// ErrSilent indicates the command already printed its error.
10+
// The error handler should exit 1 without printing anything.
11+
var ErrSilent = errors.New("silent error")
12+
13+
// ErrCancel indicates the user cancelled the operation (e.g. ctrl-c).
14+
// The error handler should exit 0.
15+
var ErrCancel = errors.New("cancelled")
16+
17+
// FlagError wraps an error caused by bad flags or arguments.
18+
// The error handler should print the error and show usage.
19+
type FlagError struct {
20+
Err error
21+
}
22+
23+
func (e *FlagError) Error() string { return e.Err.Error() }
24+
func (e *FlagError) Unwrap() error { return e.Err }
25+
26+
// NewFlagError creates a FlagError.
27+
func NewFlagError(err error) *FlagError {
28+
return &FlagError{Err: err}
29+
}
30+
31+
// HandleError handles a command error by type.
32+
// FlagError prints the error (usage is shown by cobra).
33+
// SilentError exits without printing.
34+
// CancelError exits with code 0.
35+
// Other errors print "Error: <message>".
36+
func HandleError(err error) {
37+
if err == nil {
38+
return
39+
}
40+
41+
switch {
42+
case errors.Is(err, ErrCancel):
43+
os.Exit(0)
44+
case errors.Is(err, ErrSilent):
45+
os.Exit(1)
46+
default:
47+
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
48+
os.Exit(1)
49+
}
50+
}

0 commit comments

Comments
 (0)