// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package errors import ( "fmt" "reflect" "runtime" ) // Err holds a description of an error along with information about // where the error was created. // // It may be embedded in custom error types to add extra information that // this errors package can understand. type Err struct { // message holds an annotation of the error. message string // cause holds the cause of the error as returned // by the Cause method. cause error // previous holds the previous error in the error stack, if any. previous error // file and line hold the source code location where the error was // created. file string line int } // NewErr is used to return an Err for the purpose of embedding in other // structures. The location is not specified, and needs to be set with a call // to SetLocation. // // For example: // type FooError struct { // errors.Err // code int // } // // func NewFooError(code int) error { // err := &FooError{errors.NewErr("foo"), code} // err.SetLocation(1) // return err // } func NewErr(format string, args ...interface{}) Err { return Err{ message: fmt.Sprintf(format, args...), } } // NewErrWithCause is used to return an Err with cause by other error for the purpose of embedding in other // structures. The location is not specified, and needs to be set with a call // to SetLocation. // // For example: // type FooError struct { // errors.Err // code int // } // // func (e *FooError) Annotate(format string, args ...interface{}) error { // err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code} // err.SetLocation(1) // return err // }) func NewErrWithCause(other error, format string, args ...interface{}) Err { return Err{ message: fmt.Sprintf(format, args...), cause: Cause(other), previous: other, } } // Location is the file and line of where the error was most recently // created or annotated. func (e *Err) Location() (filename string, line int) { return e.file, e.line } // Underlying returns the previous error in the error stack, if any. A client // should not ever really call this method. It is used to build the error // stack and should not be introspected by client calls. Or more // specifically, clients should not depend on anything but the `Cause` of an // error. func (e *Err) Underlying() error { return e.previous } // The Cause of an error is the most recent error in the error stack that // meets one of these criteria: the original error that was raised; the new // error that was passed into the Wrap function; the most recently masked // error; or nil if the error itself is considered the Cause. Normally this // method is not invoked directly, but instead through the Cause stand alone // function. func (e *Err) Cause() error { return e.cause } // Message returns the message stored with the most recent location. This is // the empty string if the most recent call was Trace, or the message stored // with Annotate or Mask. func (e *Err) Message() string { return e.message } // Error implements error.Error. func (e *Err) Error() string { // We want to walk up the stack of errors showing the annotations // as long as the cause is the same. err := e.previous if !sameError(Cause(err), e.cause) && e.cause != nil { err = e.cause } switch { case err == nil: return e.message case e.message == "": return err.Error() } return fmt.Sprintf("%s: %v", e.message, err) } // Format implements fmt.Formatter // When printing errors with %+v it also prints the stack trace. // %#v unsurprisingly will print the real underlying type. func (e *Err) Format(s fmt.State, verb rune) { switch verb { case 'v': switch { case s.Flag('+'): fmt.Fprintf(s, "%s", ErrorStack(e)) return case s.Flag('#'): // avoid infinite recursion by wrapping e into a type // that doesn't implement Formatter. fmt.Fprintf(s, "%#v", (*unformatter)(e)) return } fallthrough case 's': fmt.Fprintf(s, "%s", e.Error()) case 'q': fmt.Fprintf(s, "%q", e.Error()) default: fmt.Fprintf(s, "%%!%c(%T=%s)", verb, e, e.Error()) } } // helper for Format type unformatter Err func (unformatter) Format() { /* break the fmt.Formatter interface */ } // SetLocation records the source location of the error at callDepth stack // frames above the call. func (e *Err) SetLocation(callDepth int) { _, file, line, _ := runtime.Caller(callDepth + 1) e.file = trimGoPath(file) e.line = line } // StackTrace returns one string for each location recorded in the stack of // errors. The first value is the originating error, with a line for each // other annotation or tracing of the error. func (e *Err) StackTrace() []string { return errorStack(e) } // Ideally we'd have a way to check identity, but deep equals will do. func sameError(e1, e2 error) bool { return reflect.DeepEqual(e1, e2) }