Skip to content

Commit

Permalink
feat: improve flag error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
caarlos0 committed Jan 9, 2024
1 parent fa19697 commit 64381a4
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 32 deletions.
32 changes: 1 addition & 31 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"time"

"github.com/adrg/xdg"
"github.com/caarlos0/env/v9"
Expand Down Expand Up @@ -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"))
Expand Down
65 changes: 65 additions & 0 deletions flag.go
Original file line number Diff line number Diff line change
@@ -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)
}
56 changes: 56 additions & 0 deletions flag_test.go
Original file line number Diff line number Diff line change
@@ -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())
})
}
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 64381a4

Please sign in to comment.