122 lines
3.4 KiB
Go
122 lines
3.4 KiB
Go
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)
|
|
}
|