package multierror import ( "errors" "fmt" ) // Error is an error type to track multiple errors. This is used to // accumulate errors in cases and return them as a single "error". type Error struct { Errors []error ErrorFormat ErrorFormatFunc } func (e *Error) Error() string { fn := e.ErrorFormat if fn == nil { fn = ListFormatFunc } return fn(e.Errors) } // ErrorOrNil returns an error interface if this Error represents // a list of errors, or returns nil if the list of errors is empty. This // function is useful at the end of accumulation to make sure that the value // returned represents the existence of errors. func (e *Error) ErrorOrNil() error { if e == nil { return nil } if len(e.Errors) == 0 { return nil } return e } func (e *Error) GoString() string { return fmt.Sprintf("*%#v", *e) } // WrappedErrors returns the list of errors that this Error is wrapping. It is // an implementation of the errwrap.Wrapper interface so that multierror.Error // can be used with that library. // // This method is not safe to be called concurrently. Unlike accessing the // Errors field directly, this function also checks if the multierror is nil to // prevent a null-pointer panic. It satisfies the errwrap.Wrapper interface. func (e *Error) WrappedErrors() []error { if e == nil { return nil } return e.Errors } // Unwrap returns an error from Error (or nil if there are no errors). // This error returned will further support Unwrap to get the next error, // etc. The order will match the order of Errors in the multierror.Error // at the time of calling. // // The resulting error supports errors.As/Is/Unwrap so you can continue // to use the stdlib errors package to introspect further. // // This will perform a shallow copy of the errors slice. Any errors appended // to this error after calling Unwrap will not be available until a new // Unwrap is called on the multierror.Error. func (e *Error) Unwrap() error { // If we have no errors then we do nothing if e == nil || len(e.Errors) == 0 { return nil } // If we have exactly one error, we can just return that directly. if len(e.Errors) == 1 { return e.Errors[0] } // Shallow copy the slice errs := make([]error, len(e.Errors)) copy(errs, e.Errors) return chain(errs) } // chain implements the interfaces necessary for errors.Is/As/Unwrap to // work in a deterministic way with multierror. A chain tracks a list of // errors while accounting for the current represented error. This lets // Is/As be meaningful. // // Unwrap returns the next error. In the cleanest form, Unwrap would return // the wrapped error here but we can't do that if we want to properly // get access to all the errors. Instead, users are recommended to use // Is/As to get the correct error type out. // // Precondition: []error is non-empty (len > 0) type chain []error // Error implements the error interface func (e chain) Error() string { return e[0].Error() } // Unwrap implements errors.Unwrap by returning the next error in the // chain or nil if there are no more errors. func (e chain) Unwrap() error { if len(e) == 1 { return nil } return e[1:] } // As implements errors.As by attempting to map to the current value. func (e chain) As(target interface{}) bool { return errors.As(e[0], target) } // Is implements errors.Is by comparing the current value directly. func (e chain) Is(target error) bool { return errors.Is(e[0], target) }