aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEthan P. <[email protected]>2025-04-21 21:29:30 -0700
committerEthan P. <[email protected]>2025-04-21 22:06:33 -0700
commit8d771585bd8aed7c04b9d409f30599f3c7ed39c7 (patch)
treed0feb9a166a36d95befb86139f5d1f8a0ef82172
parente9268909c269ea54836435ff420ce0d5d59324ac (diff)
feat: Use error structs for most returned errors
This allows callers to differentiate between error types without having to parse the error message string.
-rw-r--r--errors.go97
-rw-r--r--flag.go52
2 files changed, 127 insertions, 22 deletions
diff --git a/errors.go b/errors.go
new file mode 100644
index 0000000..4d72a93
--- /dev/null
+++ b/errors.go
@@ -0,0 +1,97 @@
+package pflag
+
+import "fmt"
+
+// notExistErrorMessageType specifies which flavor of "flag does not exist"
+// is printed by NotExistError. This allows the related errors to be grouped
+// under a single NotExistError struct without making a breaking change to
+// the error message text.
+type notExistErrorMessageType int
+
+const (
+ flagNotExistMessage notExistErrorMessageType = iota
+ flagNotDefinedMessage
+ flagNoSuchFlagMessage
+ flagUnknownFlagMessage
+ flagUnknownShorthandFlagMessage
+)
+
+// NotExistError is the error returned when trying to access a flag that
+// does not exist in the FlagSet.
+type NotExistError struct {
+ name string
+ specifiedShorthands string
+ messageType notExistErrorMessageType
+}
+
+// Error implements error.
+func (e *NotExistError) Error() string {
+ switch e.messageType {
+ case flagNotExistMessage:
+ return fmt.Sprintf("flag %q does not exist", e.name)
+
+ case flagNotDefinedMessage:
+ return fmt.Sprintf("flag accessed but not defined: %s", e.name)
+
+ case flagNoSuchFlagMessage:
+ return fmt.Sprintf("no such flag -%v", e.name)
+
+ case flagUnknownFlagMessage:
+ return fmt.Sprintf("unknown flag: --%s", e.name)
+
+ case flagUnknownShorthandFlagMessage:
+ c := rune(e.name[0])
+ return fmt.Sprintf("unknown shorthand flag: %q in -%s", c, e.specifiedShorthands)
+ }
+
+ panic(fmt.Errorf("unknown flagNotExistErrorMessageType: %v", e.messageType))
+}
+
+// ValueRequiredError is the error returned when a flag needs an argument but
+// no argument was provided.
+type ValueRequiredError struct {
+ flag *Flag
+ specifiedName string
+ specifiedShorthands string
+}
+
+// Error implements error.
+func (e *ValueRequiredError) Error() string {
+ if len(e.specifiedShorthands) > 0 {
+ c := rune(e.specifiedName[0])
+ return fmt.Sprintf("flag needs an argument: %q in -%s", c, e.specifiedShorthands)
+ }
+
+ return fmt.Sprintf("flag needs an argument: --%s", e.specifiedName)
+}
+
+// InvalidValueError is the error returned when an invalid value is used
+// for a flag.
+type InvalidValueError struct {
+ flag *Flag
+ value string
+ cause error
+}
+
+// Error implements error.
+func (e *InvalidValueError) Error() string {
+ flag := e.flag
+ var flagName string
+ if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
+ flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name)
+ } else {
+ flagName = fmt.Sprintf("--%s", flag.Name)
+ }
+ return fmt.Sprintf("invalid argument %q for %q flag: %v", e.value, flagName, e.cause)
+}
+
+// InvalidSyntaxError is the error returned when a bad flag name is passed on
+// the command line.
+type InvalidSyntaxError struct {
+ specifiedFlag string
+}
+
+// Error implements error.
+func (e *InvalidSyntaxError) Error() string {
+ return fmt.Sprintf("bad flag syntax: %s", e.specifiedFlag)
+}
diff --git a/flag.go b/flag.go
index 4bdbd0c..80bd580 100644
--- a/flag.go
+++ b/flag.go
@@ -381,7 +381,7 @@ func (f *FlagSet) lookup(name NormalizedName) *Flag {
func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) {
flag := f.Lookup(name)
if flag == nil {
- err := fmt.Errorf("flag accessed but not defined: %s", name)
+ err := &NotExistError{name: name, messageType: flagNotDefinedMessage}
return nil, err
}
@@ -411,7 +411,7 @@ func (f *FlagSet) ArgsLenAtDash() int {
func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
flag := f.Lookup(name)
if flag == nil {
- return fmt.Errorf("flag %q does not exist", name)
+ return &NotExistError{name: name, messageType: flagNotExistMessage}
}
if usageMessage == "" {
return fmt.Errorf("deprecated message for flag %q must be set", name)
@@ -427,7 +427,7 @@ func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error {
flag := f.Lookup(name)
if flag == nil {
- return fmt.Errorf("flag %q does not exist", name)
+ return &NotExistError{name: name, messageType: flagNotExistMessage}
}
if usageMessage == "" {
return fmt.Errorf("deprecated message for flag %q must be set", name)
@@ -441,7 +441,7 @@ func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) erro
func (f *FlagSet) MarkHidden(name string) error {
flag := f.Lookup(name)
if flag == nil {
- return fmt.Errorf("flag %q does not exist", name)
+ return &NotExistError{name: name, messageType: flagNotExistMessage}
}
flag.Hidden = true
return nil
@@ -464,18 +464,16 @@ func (f *FlagSet) Set(name, value string) error {
normalName := f.normalizeFlagName(name)
flag, ok := f.formal[normalName]
if !ok {
- return fmt.Errorf("no such flag -%v", name)
+ return &NotExistError{name: name, messageType: flagNoSuchFlagMessage}
}
err := flag.Value.Set(value)
if err != nil {
- var flagName string
- if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
- flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name)
- } else {
- flagName = fmt.Sprintf("--%s", flag.Name)
+ return &InvalidValueError{
+ flag: flag,
+ value: value,
+ cause: err,
}
- return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err)
}
if !flag.Changed {
@@ -501,7 +499,7 @@ func (f *FlagSet) SetAnnotation(name, key string, values []string) error {
normalName := f.normalizeFlagName(name)
flag, ok := f.formal[normalName]
if !ok {
- return fmt.Errorf("no such flag -%v", name)
+ return &NotExistError{name: name, messageType: flagNoSuchFlagMessage}
}
if flag.Annotations == nil {
flag.Annotations = map[string][]string{}
@@ -911,10 +909,9 @@ func VarP(value Value, name, shorthand, usage string) {
CommandLine.VarP(value, name, shorthand, usage)
}
-// failf prints to standard error a formatted error and usage message and
+// fail prints an error message and usage message to standard error and
// returns the error.
-func (f *FlagSet) failf(format string, a ...interface{}) error {
- err := fmt.Errorf(format, a...)
+func (f *FlagSet) fail(err error) error {
if f.errorHandling != ContinueOnError {
fmt.Fprintln(f.Output(), err)
f.usage()
@@ -960,7 +957,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
a = args
name := s[2:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
- err = f.failf("bad flag syntax: %s", s)
+ err = f.fail(&InvalidSyntaxError{specifiedFlag: s})
return
}
@@ -982,7 +979,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
return stripUnknownFlagValue(a), nil
default:
- err = f.failf("unknown flag: --%s", name)
+ err = f.fail(&NotExistError{name: name, messageType: flagUnknownFlagMessage})
return
}
}
@@ -1000,13 +997,16 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
a = a[1:]
} else {
// '--flag' (arg was required)
- err = f.failf("flag needs an argument: %s", s)
+ err = f.fail(&ValueRequiredError{
+ flag: flag,
+ specifiedName: name,
+ })
return
}
err = fn(flag, value)
if err != nil {
- f.failf(err.Error())
+ f.fail(err)
}
return
}
@@ -1039,7 +1039,11 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
outArgs = stripUnknownFlagValue(outArgs)
return
default:
- err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands)
+ err = f.fail(&NotExistError{
+ name: string(c),
+ specifiedShorthands: shorthands,
+ messageType: flagUnknownShorthandFlagMessage,
+ })
return
}
}
@@ -1062,7 +1066,11 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
outArgs = args[1:]
} else {
// '-f' (arg was required)
- err = f.failf("flag needs an argument: %q in -%s", c, shorthands)
+ err = f.fail(&ValueRequiredError{
+ flag: flag,
+ specifiedName: string(c),
+ specifiedShorthands: shorthands,
+ })
return
}
@@ -1072,7 +1080,7 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
err = fn(flag, value)
if err != nil {
- f.failf(err.Error())
+ f.fail(err)
}
return
}