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 } // target is the template itself, therefore they are considered equal to another if tTmpl, ok := target.(*ErrTmpl); ok { return tTmpl == 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) } }