diff --git a/config.go b/config.go index a9f27620..b27ff774 100644 --- a/config.go +++ b/config.go @@ -5,8 +5,8 @@ import ( "fmt" "os" "path/filepath" - "strings" "text/template" + "time" "github.com/adrg/xdg" "github.com/caarlos0/env/v9" @@ -127,36 +127,6 @@ type Config struct { cacheReadFromID, cacheWriteToID, cacheWriteToTitle string } -type flagParseError struct { - err error -} - -func (f flagParseError) Error() string { - return fmt.Sprintf("missing flag: %s", f.Flag()) -} - -func (f flagParseError) ReasonFormat() string { - s := f.err.Error() - switch { - case strings.Contains(s, "flag needs an argument"): - return "Flag %s needs an argument." - default: - return "Flag %s is missing." - } -} - -func (f flagParseError) Flag() string { - ps := strings.Split(f.err.Error(), "-") - switch len(ps) { - case 2: //nolint:gomnd - return "-" + ps[len(ps)-1] - case 3: //nolint:gomnd - return "--" + ps[len(ps)-1] - default: - return "" - } -} - func ensureConfig() (Config, error) { var c Config sp, err := xdg.ConfigFile(filepath.Join("mods", "mods.yml")) diff --git a/flag.go b/flag.go new file mode 100644 index 00000000..013784b0 --- /dev/null +++ b/flag.go @@ -0,0 +1,65 @@ +package main + +import ( + "regexp" + "strings" + "time" + + "github.com/caarlos0/duration" +) + +func newFlagParseError(err error) flagParseError { + var reason, flag string + s := err.Error() + switch { + case strings.HasPrefix(s, "flag needs an argument:"): + reason = "Flag %s needs an argument." + ps := strings.Split(s, "-") + switch len(ps) { + case 2: //nolint:gomnd + flag = "-" + ps[len(ps)-1] + case 3: //nolint:gomnd + flag = "--" + ps[len(ps)-1] + } + case strings.HasPrefix(s, "unknown flag:"): + reason = "Flag %s is missing." + flag = strings.TrimPrefix(s, "unknown flag: ") + case strings.HasPrefix(s, "invalid argument"): + reason = "Flag %s have an invalid argument." + re := regexp.MustCompile(`invalid argument ".*" for "(.*)" flag: .*`) + parts := re.FindStringSubmatch(s) + if len(parts) > 1 { + flag = parts[1] + } + default: + reason = s + } + return flagParseError{ + err: err, + reason: reason, + flag: flag, + } +} + +type flagParseError struct { + err error + reason string + flag string +} + +func (f flagParseError) Error() string { + return f.err.Error() +} + +func (f flagParseError) ReasonFormat() string { + return f.reason +} + +func (f flagParseError) Flag() string { + return f.flag +} + +func newDurationFlag(val time.Duration, p *time.Duration) *durationFlag { + *p = val + return (*durationFlag)(p) +} diff --git a/flag_test.go b/flag_test.go new file mode 100644 index 00000000..deeac232 --- /dev/null +++ b/flag_test.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +var flagParseErrorTests = []struct { + in string + flag string + reason string +}{ + { + "unknown flag: --nope", + "--nope", + "Flag %s is missing.", + }, + { + "flag needs an argument: --delete", + "--delete", + "Flag %s needs an argument.", + }, + { + "flag needs an argument: 'd' in -d", + "-d", + "Flag %s needs an argument.", + }, + { + `invalid argument "20dd" for "--delete-older-than" flag: time: unknown unit "dd" in duration "20dd"`, + "--delete-older-than", + "Flag %s have an invalid argument.", + }, + { + `invalid argument "sdfjasdl" for "--max-tokens" flag: strconv.ParseInt: parsing "sdfjasdl": invalid syntax`, + "--max-tokens", + "Flag %s have an invalid argument.", + }, + { + `invalid argument "nope" for "-r, --raw" flag: strconv.ParseBool: parsing "nope": invalid syntax`, + "-r, --raw", + "Flag %s have an invalid argument.", + }, +} + +func TestFlagParseError(t *testing.T) { + for _, tf := range flagParseErrorTests { + t.Run(tf.in, func(t *testing.T) { + err := newFlagParseError(fmt.Errorf(tf.in)) + require.Equal(t, tf.flag, err.Flag()) + require.Equal(t, tf.reason, err.ReasonFormat()) + require.Equal(t, tf.in, err.Error()) + }) + } +} diff --git a/main.go b/main.go index b8e82a60..3f79e740 100644 --- a/main.go +++ b/main.go @@ -54,7 +54,7 @@ func init() { buildVersion() rootCmd.SetUsageFunc(usageFunc) rootCmd.SetFlagErrorFunc(func(_ *cobra.Command, err error) error { - return flagParseError{err: err} + return newFlagParseError(err) }) rootCmd.CompletionOptions.HiddenDefaultCmd = true