2020-09-09 11:48:46 +02:00
|
|
|
package adverr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Error is a wrapper for error with stack trace
|
|
|
|
type Error struct {
|
|
|
|
msg string
|
|
|
|
callTrace *CallTrace
|
|
|
|
tmpl *ErrTmpl
|
|
|
|
cause error
|
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a new Error with the given message
|
|
|
|
func New(msg string) *Error {
|
|
|
|
return &Error{
|
|
|
|
msg: msg,
|
|
|
|
cause: nil,
|
|
|
|
tmpl: nil,
|
|
|
|
callTrace: Trace(2),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wrap returns a new Error with the given message which is caused by cause
|
|
|
|
func Wrap(msg string, cause error) *Error {
|
|
|
|
return &Error{
|
|
|
|
msg: msg,
|
|
|
|
cause: cause,
|
|
|
|
callTrace: Trace(2),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func errtype(err error) string {
|
|
|
|
if e, ok := err.(*Error); ok && e.tmpl != nil {
|
|
|
|
return errtype(e.tmpl)
|
|
|
|
} else if tmpl, ok := err.(*ErrTmpl); ok {
|
2020-09-09 13:34:23 +02:00
|
|
|
return tmpl.name
|
2020-09-09 11:48:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
t := reflect.TypeOf(err)
|
|
|
|
for t.Kind() == reflect.Ptr {
|
|
|
|
t = t.Elem()
|
|
|
|
}
|
|
|
|
return t.PkgPath() + "." + t.Name()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Error) Unwrap() error {
|
|
|
|
return e.cause
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Error) Error() string {
|
|
|
|
b := new(strings.Builder)
|
|
|
|
printErr(e, b)
|
|
|
|
return b.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is implements the error equality function used by errors.Is()
|
|
|
|
// It returns true if the error is the same instance or is created using the same ErrTmpl
|
|
|
|
func (e *Error) Is(target error) bool {
|
|
|
|
// same error instance
|
|
|
|
if target == e {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// no template used, therefore no equality possible
|
|
|
|
if e.tmpl == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// same template, therefore errors are equal to another
|
|
|
|
if tErr, ok := target.(*Error); ok {
|
|
|
|
return tErr.tmpl == e.tmpl
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func printErr(err error, b *strings.Builder) {
|
|
|
|
if e, ok := err.(*Error); ok {
|
|
|
|
b.WriteString(errtype(e))
|
|
|
|
b.WriteString(": ")
|
|
|
|
b.WriteString(e.msg)
|
|
|
|
b.WriteString("\n")
|
|
|
|
b.WriteString(e.callTrace.String())
|
|
|
|
} else {
|
|
|
|
b.WriteString(errtype(err))
|
|
|
|
b.WriteString(": ")
|
|
|
|
b.WriteString(err.Error())
|
|
|
|
b.WriteString("\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
cause := errors.Unwrap(err)
|
|
|
|
if cause != nil {
|
|
|
|
b.WriteString("Caused by ")
|
|
|
|
printErr(cause, b)
|
|
|
|
}
|
|
|
|
}
|