1 package multierror 2 3 import ( 4 "errors" 5 "fmt" 6 ) 7 8 // Error is an error type to track multiple errors. This is used to 9 // accumulate errors in cases and return them as a single "error". 10 type Error struct { 11 Errors []error 12 ErrorFormat ErrorFormatFunc 13 } 14 15 func (e *Error) Error() string { 16 fn := e.ErrorFormat 17 if fn == nil { 18 fn = ListFormatFunc 19 } 20 21 return fn(e.Errors) 22 } 23 24 // ErrorOrNil returns an error interface if this Error represents 25 // a list of errors, or returns nil if the list of errors is empty. This 26 // function is useful at the end of accumulation to make sure that the value 27 // returned represents the existence of errors. 28 func (e *Error) ErrorOrNil() error { 29 if e == nil { 30 return nil 31 } 32 if len(e.Errors) == 0 { 33 return nil 34 } 35 36 return e 37 } 38 39 func (e *Error) GoString() string { 40 return fmt.Sprintf("*%#v", *e) 41 } 42 43 // WrappedErrors returns the list of errors that this Error is wrapping. It is 44 // an implementation of the errwrap.Wrapper interface so that multierror.Error 45 // can be used with that library. 46 // 47 // This method is not safe to be called concurrently. Unlike accessing the 48 // Errors field directly, this function also checks if the multierror is nil to 49 // prevent a null-pointer panic. It satisfies the errwrap.Wrapper interface. 50 func (e *Error) WrappedErrors() []error { 51 if e == nil { 52 return nil 53 } 54 return e.Errors 55 } 56 57 // Unwrap returns an error from Error (or nil if there are no errors). 58 // This error returned will further support Unwrap to get the next error, 59 // etc. The order will match the order of Errors in the multierror.Error 60 // at the time of calling. 61 // 62 // The resulting error supports errors.As/Is/Unwrap so you can continue 63 // to use the stdlib errors package to introspect further. 64 // 65 // This will perform a shallow copy of the errors slice. Any errors appended 66 // to this error after calling Unwrap will not be available until a new 67 // Unwrap is called on the multierror.Error. 68 func (e *Error) Unwrap() error { 69 // If we have no errors then we do nothing 70 if e == nil || len(e.Errors) == 0 { 71 return nil 72 } 73 74 // If we have exactly one error, we can just return that directly. 75 if len(e.Errors) == 1 { 76 return e.Errors[0] 77 } 78 79 // Shallow copy the slice 80 errs := make([]error, len(e.Errors)) 81 copy(errs, e.Errors) 82 return chain(errs) 83 } 84 85 // chain implements the interfaces necessary for errors.Is/As/Unwrap to 86 // work in a deterministic way with multierror. A chain tracks a list of 87 // errors while accounting for the current represented error. This lets 88 // Is/As be meaningful. 89 // 90 // Unwrap returns the next error. In the cleanest form, Unwrap would return 91 // the wrapped error here but we can't do that if we want to properly 92 // get access to all the errors. Instead, users are recommended to use 93 // Is/As to get the correct error type out. 94 // 95 // Precondition: []error is non-empty (len > 0) 96 type chain []error 97 98 // Error implements the error interface 99 func (e chain) Error() string { 100 return e[0].Error() 101 } 102 103 // Unwrap implements errors.Unwrap by returning the next error in the 104 // chain or nil if there are no more errors. 105 func (e chain) Unwrap() error { 106 if len(e) == 1 { 107 return nil 108 } 109 110 return e[1:] 111 } 112 113 // As implements errors.As by attempting to map to the current value. 114 func (e chain) As(target interface{}) bool { 115 return errors.As(e[0], target) 116 } 117 118 // Is implements errors.Is by comparing the current value directly. 119 func (e chain) Is(target error) bool { 120 return errors.Is(e[0], target) 121 } 122