// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package errors import ( "fmt" "reflect" ) // 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 // function is the package path-qualified function name where the // error was created. function string // line is the line number the error was created on inside of function line int } // Locationer is an interface that represents a certain class of errors that // contain the location information from where they were raised. type Locationer interface { // Location returns the path-qualified function name where the error was // created and the line number Location() (function string, line int) } // locationError is the internal implementation of the Locationer interface. type locationError struct { error // function is the package path-qualified function name where the // error was created. function string // line is the line number the error was created on inside of function line int } // newLocationError constructs a new Locationer error from the supplied error // with the location set to callDepth in the stack. If a nill error is provided // to this function then a new empty error is constructed. func newLocationError(err error, callDepth int) *locationError { le := &locationError{error: err} le.function, le.line = getLocation(callDepth + 1) return le } // Error implementes the error interface. func (l *locationError) Error() string { if l.error == nil { return "" } return l.error.Error() } // *locationError implements Locationer.Location interface func (l *locationError) Location() (string, int) { return l.function, l.line } func (l *locationError) Unwrap() error { return l.error } // 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 returns the package path-qualified function name and line of where // the error was most recently created or annotated. func (e *Err) Location() (function string, line int) { return e.function, 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 } // Cause returns 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 package path-qualified function name of the error at // callDepth stack frames above the call. func (e *Err) SetLocation(callDepth int) { e.function, e.line = getLocation(callDepth + 1) } // 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) } // Unwrap is a synonym for Underlying, which allows Err to be used with the // Unwrap, Is and As functions in Go's standard `errors` library. func (e *Err) Unwrap() error { return e.previous }