adverr/error.go
2020-09-09 14:06:31 +02:00

101 lines
2.0 KiB
Go

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 {
return tmpl.name
}
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\t(Unknown source)\n")
}
cause := errors.Unwrap(err)
if cause != nil {
b.WriteString("Caused by ")
printErr(cause, b)
}
}