diff --git a/bin/.go@latest.pkg b/bin/.go-1.17.pkg similarity index 100% rename from bin/.go@latest.pkg rename to bin/.go-1.17.pkg diff --git a/bin/go b/bin/go index 142d6ff..cdc9b02 120000 --- a/bin/go +++ b/bin/go @@ -1 +1 @@ -.go@latest.pkg \ No newline at end of file +.go-1.17.pkg \ No newline at end of file diff --git a/bin/gofmt b/bin/gofmt index 142d6ff..cdc9b02 120000 --- a/bin/gofmt +++ b/bin/gofmt @@ -1 +1 @@ -.go@latest.pkg \ No newline at end of file +.go-1.17.pkg \ No newline at end of file diff --git a/callbacks.go b/callbacks.go index 8b78741..1cfaccf 100644 --- a/callbacks.go +++ b/callbacks.go @@ -4,8 +4,6 @@ import ( "fmt" "reflect" "strings" - - "github.com/pkg/errors" ) type bindings map[reflect.Type]func() (reflect.Value, error) @@ -35,7 +33,7 @@ func (b bindings) addProvider(provider interface{}) error { pv := reflect.ValueOf(provider) t := pv.Type() if t.Kind() != reflect.Func || t.NumIn() != 0 || t.NumOut() != 2 || t.Out(1) != reflect.TypeOf((*error)(nil)).Elem() { - return errors.Errorf("%T must be a function with the signature func()(T, error)", provider) + return fmt.Errorf("%T must be a function with the signature func()(T, error)", provider) } rt := pv.Type().Out(0) b[rt] = func() (reflect.Value, error) { diff --git a/context.go b/context.go index 6d48f5f..45c412a 100644 --- a/context.go +++ b/context.go @@ -1,14 +1,13 @@ package kong import ( + "errors" "fmt" "os" "reflect" "sort" "strconv" "strings" - - "github.com/pkg/errors" ) // Path records the nodes and parsed values from the current command-line. @@ -209,9 +208,8 @@ func (c *Context) Validate() error { // nolint: gocyclo desc = node.Path() } if validate := isValidatable(value); validate != nil { - err := validate.Validate() - if err != nil { - return errors.Wrap(err, desc) + if err := validate.Validate(); err != nil { + return fmt.Errorf("%s: %w", desc, err) } } } @@ -558,7 +556,7 @@ func (c *Context) Resolve() error { for _, resolver := range resolvers { s, err := resolver.Resolve(c, path, flag) if err != nil { - return errors.Wrap(err, flag.ShortSummary()) + return fmt.Errorf("%s: %w", flag.ShortSummary(), err) } if s == nil { continue @@ -683,8 +681,9 @@ func (c *Context) parseFlag(flags []*Flag, match string) (err error) { } err := flag.Parse(c.scan, c.getValue(flag.Value)) if err != nil { - if e, ok := errors.Cause(err).(*expectedError); ok && e.token.InferredType().IsAny(FlagToken, ShortFlagToken) { - return errors.Errorf("%s; perhaps try %s=%q?", err, flag.ShortSummary(), e.token) + var expected *expectedError + if errors.As(err, &expected) && expected.token.InferredType().IsAny(FlagToken, ShortFlagToken) { + return fmt.Errorf("%s; perhaps try %s=%q?", err.Error(), flag.ShortSummary(), expected.token) } return err } @@ -888,7 +887,7 @@ func checkEnum(value *Value, target reflect.Value) error { return nil case reflect.Map, reflect.Struct: - return errors.Errorf("enum can only be applied to a slice or value") + return errors.New("enum can only be applied to a slice or value") default: enumSlice := value.EnumSlice() diff --git a/error.go b/error.go index 30b8858..18225ef 100644 --- a/error.go +++ b/error.go @@ -8,5 +8,5 @@ type ParseError struct { Context *Context } -// Cause returns the original cause of the error. -func (p *ParseError) Cause() error { return p.error } +// Unwrap returns the original cause of the error. +func (p *ParseError) Unwrap() error { return p.error } diff --git a/go.mod b/go.mod index bdd8bb2..e5c0722 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/alecthomas/kong require ( github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index 12908f3..e787c1f 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,6 @@ github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/kong.go b/kong.go index 3123dc2..eeeaa8c 100644 --- a/kong.go +++ b/kong.go @@ -1,6 +1,7 @@ package kong import ( + "errors" "fmt" "io" "os" @@ -378,13 +379,14 @@ func (k *Kong) FatalIfErrorf(err error, args ...interface{}) { msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error() } // Maybe display usage information. - if err, ok := err.(*ParseError); ok { + var parseErr *ParseError + if errors.As(err, &parseErr) { switch k.usageOnError { case fullUsage: - _ = k.help(k.helpOptions, err.Context) + _ = k.help(k.helpOptions, parseErr.Context) fmt.Fprintln(k.Stdout) case shortUsage: - _ = k.shortHelp(k.helpOptions, err.Context) + _ = k.shortHelp(k.helpOptions, parseErr.Context) fmt.Fprintln(k.Stdout) } } diff --git a/kong_test.go b/kong_test.go index 6e52f5a..6ce73f5 100644 --- a/kong_test.go +++ b/kong_test.go @@ -2,11 +2,11 @@ package kong_test import ( "bytes" + "errors" "fmt" "strings" "testing" - "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/alecthomas/kong" diff --git a/mapper.go b/mapper.go index 88fe1a5..139b2df 100644 --- a/mapper.go +++ b/mapper.go @@ -3,6 +3,7 @@ package kong import ( "encoding" "encoding/json" + "errors" "fmt" "io/ioutil" "math/bits" @@ -12,8 +13,6 @@ import ( "strconv" "strings" "time" - - "github.com/pkg/errors" ) var ( @@ -295,14 +294,14 @@ func (boolMapper) Decode(ctx *DecodeContext, target reflect.Value) error { target.SetBool(false) default: - return errors.Errorf("bool value must be true, 1, yes, false, 0 or no but got %q", v) + return fmt.Errorf("bool value must be true, 1, yes, false, 0 or no but got %q", v) } case bool: target.SetBool(v) default: - return errors.Errorf("expected bool but got %q (%T)", token.Value, token.Value) + return fmt.Errorf("expected bool but got %q (%T)", token.Value, token.Value) } } else { target.SetBool(true) @@ -322,12 +321,12 @@ func durationDecoder() MapperFunc { case string: d, err = time.ParseDuration(v) if err != nil { - return errors.Errorf("expected duration but got %q: %s", v, err) + return fmt.Errorf("expected duration but got %q: %v", v, err) } case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: d = reflect.ValueOf(v).Convert(reflect.TypeOf(time.Duration(0))).Interface().(time.Duration) // nolint: forcetypeassert default: - return errors.Errorf("expected duration but got %q", v) + return fmt.Errorf("expected duration but got %q", v) } target.Set(reflect.ValueOf(d)) return nil @@ -368,11 +367,11 @@ func intDecoder(bits int) MapperFunc { // nolint: dupl sv = fmt.Sprintf("%v", v) default: - return errors.Errorf("expected an int but got %q (%T)", t, t.Value) + return fmt.Errorf("expected an int but got %q (%T)", t, t.Value) } n, err := strconv.ParseInt(sv, 10, bits) if err != nil { - return errors.Errorf("expected a valid %d bit int but got %q", bits, sv) + return fmt.Errorf("expected a valid %d bit int but got %q", bits, sv) } target.SetInt(n) return nil @@ -394,11 +393,11 @@ func uintDecoder(bits int) MapperFunc { // nolint: dupl sv = fmt.Sprintf("%v", v) default: - return errors.Errorf("expected an int but got %q (%T)", t, t.Value) + return fmt.Errorf("expected an int but got %q (%T)", t, t.Value) } n, err := strconv.ParseUint(sv, 10, bits) if err != nil { - return errors.Errorf("expected a valid %d bit uint but got %q", bits, sv) + return fmt.Errorf("expected a valid %d bit uint but got %q", bits, sv) } target.SetUint(n) return nil @@ -415,7 +414,7 @@ func floatDecoder(bits int) MapperFunc { case string: n, err := strconv.ParseFloat(v, bits) if err != nil { - return errors.Errorf("expected a float but got %q (%T)", t, t.Value) + return fmt.Errorf("expected a float but got %q (%T)", t, t.Value) } target.SetFloat(n) @@ -429,7 +428,7 @@ func floatDecoder(bits int) MapperFunc { target.Set(reflect.ValueOf(v)) default: - return errors.Errorf("expected an int but got %q (%T)", t, t.Value) + return fmt.Errorf("expected an int but got %q (%T)", t, t.Value) } return nil } @@ -447,7 +446,7 @@ func mapDecoder(r *Registry) MapperFunc { t := ctx.Scan.Pop() // If decoding a flag, we need an argument. if t.IsEOL() { - return errors.Errorf("unexpected EOL") + return errors.New("unexpected EOL") } switch v := t.Value.(type) { case string: @@ -457,7 +456,7 @@ func mapDecoder(r *Registry) MapperFunc { for _, m := range v { err := jsonTranscode(m, target.Addr().Interface()) if err != nil { - return errors.WithStack(err) + return err } } return nil @@ -466,7 +465,7 @@ func mapDecoder(r *Registry) MapperFunc { return jsonTranscode(v, target.Addr().Interface()) default: - return errors.Errorf("invalid map value %q (of type %T)", t, t.Value) + return fmt.Errorf("invalid map value %q (of type %T)", t, t.Value) } } else { tokens := ctx.Scan.PopWhile(func(t Token) bool { return t.IsValue() }) @@ -480,7 +479,7 @@ func mapDecoder(r *Registry) MapperFunc { } parts := strings.SplitN(token, "=", 2) if len(parts) != 2 { - return errors.Errorf("expected \"=\" but got %q", token) + return fmt.Errorf("expected \"=\" but got %q", token) } key, value := parts[0], parts[1] @@ -488,7 +487,7 @@ func mapDecoder(r *Registry) MapperFunc { if typ := ctx.Value.Tag.Type; typ != "" { parts := strings.Split(typ, ":") if len(parts) != 2 { - return errors.Errorf("type:\"\" on map field must be in the form \"[]:[]\"") + return errors.New("type:\"\" on map field must be in the form \"[]:[]\"") } keyTypeName, valueTypeName = parts[0], parts[1] } @@ -497,14 +496,14 @@ func mapDecoder(r *Registry) MapperFunc { keyDecoder := r.ForNamedType(keyTypeName, el.Key()) keyValue := reflect.New(el.Key()).Elem() if err := keyDecoder.Decode(ctx.WithScanner(keyScanner), keyValue); err != nil { - return errors.Errorf("invalid map key %q", key) + return fmt.Errorf("invalid map key %q", key) } valueScanner := Scan(value) valueDecoder := r.ForNamedType(valueTypeName, el.Elem()) valueValue := reflect.New(el.Elem()).Elem() if err := valueDecoder.Decode(ctx.WithScanner(valueScanner), valueValue); err != nil { - return errors.Errorf("invalid map value %q", value) + return fmt.Errorf("invalid map value %q", value) } target.SetMapIndex(keyValue, valueValue) @@ -522,7 +521,7 @@ func sliceDecoder(r *Registry) MapperFunc { t := ctx.Scan.Pop() // If decoding a flag, we need an argument. if t.IsEOL() { - return errors.Errorf("unexpected EOL") + return errors.New("unexpected EOL") } switch v := t.Value.(type) { case string: @@ -541,13 +540,13 @@ func sliceDecoder(r *Registry) MapperFunc { } childDecoder := r.ForNamedType(ctx.Value.Tag.Type, el) if childDecoder == nil { - return errors.Errorf("no mapper for element type of %s", target.Type()) + return fmt.Errorf("no mapper for element type of %s", target.Type()) } for !childScanner.Peek().IsEOL() { childValue := reflect.New(el).Elem() err := childDecoder.Decode(ctx.WithScanner(childScanner), childValue) if err != nil { - return errors.WithStack(err) + return err } target.Set(reflect.Append(target, childValue)) } @@ -561,7 +560,7 @@ func pathMapper(r *Registry) MapperFunc { return sliceDecoder(r)(ctx, target) } if target.Kind() != reflect.String { - return errors.Errorf("\"path\" type must be applied to a string not %s", target.Type()) + return fmt.Errorf("\"path\" type must be applied to a string not %s", target.Type()) } var path string err := ctx.Scan.PopValueInto("file", &path) @@ -607,7 +606,7 @@ func existingFileMapper(r *Registry) MapperFunc { return sliceDecoder(r)(ctx, target) } if target.Kind() != reflect.String { - return errors.Errorf("\"existingfile\" type must be applied to a string not %s", target.Type()) + return fmt.Errorf("\"existingfile\" type must be applied to a string not %s", target.Type()) } var path string err := ctx.Scan.PopValueInto("file", &path) @@ -621,7 +620,7 @@ func existingFileMapper(r *Registry) MapperFunc { return err } if stat.IsDir() { - return errors.Errorf("%q exists but is a directory", path) + return fmt.Errorf("%q exists but is a directory", path) } } target.SetString(path) @@ -635,7 +634,7 @@ func existingDirMapper(r *Registry) MapperFunc { return sliceDecoder(r)(ctx, target) } if target.Kind() != reflect.String { - return errors.Errorf("\"existingdir\" must be applied to a string not %s", target.Type()) + return fmt.Errorf("\"existingdir\" must be applied to a string not %s", target.Type()) } var path string err := ctx.Scan.PopValueInto("file", &path) @@ -648,7 +647,7 @@ func existingDirMapper(r *Registry) MapperFunc { return err } if !stat.IsDir() { - return errors.Errorf("%q exists but is not a directory", path) + return fmt.Errorf("%q exists but is not a directory", path) } target.SetString(path) return nil @@ -666,7 +665,7 @@ func counterMapper() MapperFunc { case string: n, err := strconv.ParseInt(v, 10, 64) if err != nil { - return errors.Errorf("expected a counter but got %q (%T)", t, t.Value) + return fmt.Errorf("expected a counter but got %q (%T)", t, t.Value) } target.SetInt(n) @@ -674,7 +673,7 @@ func counterMapper() MapperFunc { target.Set(reflect.ValueOf(v)) default: - return errors.Errorf("expected a counter but got %q (%T)", t, t.Value) + return fmt.Errorf("expected a counter but got %q (%T)", t, t.Value) } return nil } @@ -690,7 +689,7 @@ func counterMapper() MapperFunc { target.SetFloat(target.Float() + 1) default: - return errors.Errorf("type:\"counter\" must be used with a numeric field") + return fmt.Errorf("type:\"counter\" must be used with a numeric field") } return nil } @@ -705,7 +704,7 @@ func urlMapper() MapperFunc { } url, err := url.Parse(urlStr) if err != nil { - return errors.WithStack(err) + return err } target.Set(reflect.ValueOf(url)) return nil @@ -775,7 +774,7 @@ func (f *NamedFileContentFlag) Decode(ctx *DecodeContext) error { // nolint: rev filename = ExpandPath(filename) data, err := ioutil.ReadFile(filename) // nolint: gosec if err != nil { - return errors.Errorf("failed to open %q: %s", filename, err) + return fmt.Errorf("failed to open %q: %v", filename, err) } f.Contents = data f.Filename = filename @@ -799,7 +798,7 @@ func (f *FileContentFlag) Decode(ctx *DecodeContext) error { // nolint: revive filename = ExpandPath(filename) data, err := ioutil.ReadFile(filename) // nolint: gosec if err != nil { - return errors.Errorf("failed to open %q: %s", filename, err) + return fmt.Errorf("failed to open %q: %v", filename, err) } *f = data return nil @@ -808,7 +807,10 @@ func (f *FileContentFlag) Decode(ctx *DecodeContext) error { // nolint: revive func jsonTranscode(in, out interface{}) error { data, err := json.Marshal(in) if err != nil { - return errors.WithStack(err) + return err } - return errors.Wrapf(json.Unmarshal(data, out), "%#v -> %T", in, out) + if err = json.Unmarshal(data, out); err != nil { + return fmt.Errorf("%#v -> %T: %v", in, out, err) + } + return nil } diff --git a/model.go b/model.go index d20d864..af2cdaf 100644 --- a/model.go +++ b/model.go @@ -7,8 +7,6 @@ import ( "reflect" "strconv" "strings" - - "github.com/pkg/errors" ) // A Visitable component in the model. @@ -335,7 +333,7 @@ func (v *Value) Parse(scan *Scanner, target reflect.Value) (err error) { } err = v.Mapper.Decode(&DecodeContext{Value: v, Scan: scan}, target) if err != nil { - return errors.Wrap(err, v.ShortSummary()) + return fmt.Errorf("%s: %w", v.ShortSummary(), err) } v.Set = true return nil diff --git a/options.go b/options.go index 7ddb4e3..02d33fd 100644 --- a/options.go +++ b/options.go @@ -1,6 +1,8 @@ package kong import ( + "errors" + "fmt" "io" "os" "os/user" @@ -8,8 +10,6 @@ import ( "reflect" "regexp" "strings" - - "github.com/pkg/errors" ) // An Option applies optional changes to the Kong application. @@ -344,7 +344,7 @@ func IgnoreFields(regexes ...string) Option { re, err := regexp.Compile(r) if err != nil { - return errors.Wrap(err, "unable to compile regex") + return fmt.Errorf("unable to compile regex: %v", err) } k.ignoreFields = append(k.ignoreFields, re) @@ -380,7 +380,7 @@ func Configuration(loader ConfigurationLoader, paths ...string) Option { resolver, err := k.LoadConfig(path) if err != nil { - return errors.Wrap(err, path) + return fmt.Errorf("%s: %v", path, err) } if resolver != nil { k.resolvers = append(k.resolvers, resolver)