diff --git a/errors/.gitignore b/errors/.gitignore new file mode 100644 index 0000000..f417e74 --- /dev/null +++ b/errors/.gitignore @@ -0,0 +1,2 @@ +/.idea +/vendor diff --git a/errors/Gopkg.lock b/errors/Gopkg.lock new file mode 100644 index 0000000..07b0186 --- /dev/null +++ b/errors/Gopkg.lock @@ -0,0 +1,15 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/go-errors/errors" + packages = ["."] + revision = "3afebba5a48dbc89b574d890b6b34d9ee10b4785" + version = "v1.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "cdb8112723180cdbe98df0e74827b04930f26759f6c08fe39066835774c18175" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/errors/Gopkg.toml b/errors/Gopkg.toml new file mode 100644 index 0000000..5dc9c30 --- /dev/null +++ b/errors/Gopkg.toml @@ -0,0 +1,3 @@ +[[constraint]] + name = "github.com/go-errors/errors" + version = "1.0.0" diff --git a/errors/README.md b/errors/README.md new file mode 100644 index 0000000..a71fa09 --- /dev/null +++ b/errors/README.md @@ -0,0 +1,4 @@ +# errors + +Better error handling. Marries [go-errors/errors](https://github.com/go-errors/errors) to [pkg/errors](https://github.com/pkg/errors), and +adds a little bit of our own magic sauce. \ No newline at end of file diff --git a/errors/errors.go b/errors/errors.go new file mode 100644 index 0000000..aae4e04 --- /dev/null +++ b/errors/errors.go @@ -0,0 +1,89 @@ +package errors + +import ( + base "errors" + "fmt" + + "github.com/go-errors/errors" +) + +// interop with pkg/errors +type causer interface { + Cause() error +} + +// Err intelligently creates/handles errors, while preserving the stack trace. +// It works with errors from github.com/pkg/errors too. +func Err(err interface{}, fmtParams ...interface{}) error { + if err == nil { + return nil + } + + if _, ok := err.(causer); ok { + err = fmt.Errorf("%+v", err) + } else if errString, ok := err.(string); ok && len(fmtParams) > 0 { + err = fmt.Errorf(errString, fmtParams...) + } + + return errors.Wrap(err, 1) +} + +// Wrap calls errors.Wrap, in case you want to skip a different amount +func Wrap(err interface{}, skip int) *errors.Error { + if err == nil { + return nil + } + + if _, ok := err.(causer); ok { + err = fmt.Errorf("%+v", err) + } + + return errors.Wrap(err, skip+1) +} + +// Is compares two wrapped errors to determine if the underlying errors are the same +// It also interops with errors from pkg/errors +func Is(e error, original error) bool { + if c, ok := e.(causer); ok { + e = c.Cause() + } + if c, ok := original.(causer); ok { + original = c.Cause() + } + return errors.Is(e, original) +} + +// Prefix prefixes the message of the error with the given string +func Prefix(prefix string, err interface{}) error { + if err == nil { + return nil + } + return errors.WrapPrefix(Err(err), prefix, 0) +} + +// Trace returns the stack trace +func Trace(err error) string { + if err == nil { + return "" + } + return string(Err(err).(*errors.Error).Stack()) +} + +// FullTrace returns the error type, message, and stack trace +func FullTrace(err error) string { + if err == nil { + return "" + } + return Err(err).(*errors.Error).ErrorStack() +} + +// Base returns a simple error with no stack trace attached +func Base(text string) error { + return base.New(text) +} + +// HasTrace checks if error has a trace attached +func HasTrace(err error) bool { + _, ok := err.(*errors.Error) + return ok +}