| // Copyright (c) 2017-2021 Uber Technologies, Inc. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| // Package multierr allows combining one or more errors together. |
| // |
| // Overview |
| // |
| // Errors can be combined with the use of the Combine function. |
| // |
| // multierr.Combine( |
| // reader.Close(), |
| // writer.Close(), |
| // conn.Close(), |
| // ) |
| // |
| // If only two errors are being combined, the Append function may be used |
| // instead. |
| // |
| // err = multierr.Append(reader.Close(), writer.Close()) |
| // |
| // The underlying list of errors for a returned error object may be retrieved |
| // with the Errors function. |
| // |
| // errors := multierr.Errors(err) |
| // if len(errors) > 0 { |
| // fmt.Println("The following errors occurred:", errors) |
| // } |
| // |
| // Appending from a loop |
| // |
| // You sometimes need to append into an error from a loop. |
| // |
| // var err error |
| // for _, item := range items { |
| // err = multierr.Append(err, process(item)) |
| // } |
| // |
| // Cases like this may require knowledge of whether an individual instance |
| // failed. This usually requires introduction of a new variable. |
| // |
| // var err error |
| // for _, item := range items { |
| // if perr := process(item); perr != nil { |
| // log.Warn("skipping item", item) |
| // err = multierr.Append(err, perr) |
| // } |
| // } |
| // |
| // multierr includes AppendInto to simplify cases like this. |
| // |
| // var err error |
| // for _, item := range items { |
| // if multierr.AppendInto(&err, process(item)) { |
| // log.Warn("skipping item", item) |
| // } |
| // } |
| // |
| // This will append the error into the err variable, and return true if that |
| // individual error was non-nil. |
| // |
| // See AppendInto for more information. |
| // |
| // Deferred Functions |
| // |
| // Go makes it possible to modify the return value of a function in a defer |
| // block if the function was using named returns. This makes it possible to |
| // record resource cleanup failures from deferred blocks. |
| // |
| // func sendRequest(req Request) (err error) { |
| // conn, err := openConnection() |
| // if err != nil { |
| // return err |
| // } |
| // defer func() { |
| // err = multierr.Append(err, conn.Close()) |
| // }() |
| // // ... |
| // } |
| // |
| // multierr provides the Invoker type and AppendInvoke function to make cases |
| // like the above simpler and obviate the need for a closure. The following is |
| // roughly equivalent to the example above. |
| // |
| // func sendRequest(req Request) (err error) { |
| // conn, err := openConnection() |
| // if err != nil { |
| // return err |
| // } |
| // defer multierr.AppendInvoke(err, multierr.Close(conn)) |
| // // ... |
| // } |
| // |
| // See AppendInvoke and Invoker for more information. |
| // |
| // Advanced Usage |
| // |
| // Errors returned by Combine and Append MAY implement the following |
| // interface. |
| // |
| // type errorGroup interface { |
| // // Returns a slice containing the underlying list of errors. |
| // // |
| // // This slice MUST NOT be modified by the caller. |
| // Errors() []error |
| // } |
| // |
| // Note that if you need access to list of errors behind a multierr error, you |
| // should prefer using the Errors function. That said, if you need cheap |
| // read-only access to the underlying errors slice, you can attempt to cast |
| // the error to this interface. You MUST handle the failure case gracefully |
| // because errors returned by Combine and Append are not guaranteed to |
| // implement this interface. |
| // |
| // var errors []error |
| // group, ok := err.(errorGroup) |
| // if ok { |
| // errors = group.Errors() |
| // } else { |
| // errors = []error{err} |
| // } |
| package multierr // import "go.uber.org/multierr" |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| "io" |
| "strings" |
| "sync" |
| |
| "go.uber.org/atomic" |
| ) |
| |
| var ( |
| // Separator for single-line error messages. |
| _singlelineSeparator = []byte("; ") |
| |
| // Prefix for multi-line messages |
| _multilinePrefix = []byte("the following errors occurred:") |
| |
| // Prefix for the first and following lines of an item in a list of |
| // multi-line error messages. |
| // |
| // For example, if a single item is: |
| // |
| // foo |
| // bar |
| // |
| // It will become, |
| // |
| // - foo |
| // bar |
| _multilineSeparator = []byte("\n - ") |
| _multilineIndent = []byte(" ") |
| ) |
| |
| // _bufferPool is a pool of bytes.Buffers. |
| var _bufferPool = sync.Pool{ |
| New: func() interface{} { |
| return &bytes.Buffer{} |
| }, |
| } |
| |
| type errorGroup interface { |
| Errors() []error |
| } |
| |
| // Errors returns a slice containing zero or more errors that the supplied |
| // error is composed of. If the error is nil, a nil slice is returned. |
| // |
| // err := multierr.Append(r.Close(), w.Close()) |
| // errors := multierr.Errors(err) |
| // |
| // If the error is not composed of other errors, the returned slice contains |
| // just the error that was passed in. |
| // |
| // Callers of this function are free to modify the returned slice. |
| func Errors(err error) []error { |
| if err == nil { |
| return nil |
| } |
| |
| // Note that we're casting to multiError, not errorGroup. Our contract is |
| // that returned errors MAY implement errorGroup. Errors, however, only |
| // has special behavior for multierr-specific error objects. |
| // |
| // This behavior can be expanded in the future but I think it's prudent to |
| // start with as little as possible in terms of contract and possibility |
| // of misuse. |
| eg, ok := err.(*multiError) |
| if !ok { |
| return []error{err} |
| } |
| |
| errors := eg.Errors() |
| result := make([]error, len(errors)) |
| copy(result, errors) |
| return result |
| } |
| |
| // multiError is an error that holds one or more errors. |
| // |
| // An instance of this is guaranteed to be non-empty and flattened. That is, |
| // none of the errors inside multiError are other multiErrors. |
| // |
| // multiError formats to a semi-colon delimited list of error messages with |
| // %v and with a more readable multi-line format with %+v. |
| type multiError struct { |
| copyNeeded atomic.Bool |
| errors []error |
| } |
| |
| var _ errorGroup = (*multiError)(nil) |
| |
| // Errors returns the list of underlying errors. |
| // |
| // This slice MUST NOT be modified. |
| func (merr *multiError) Errors() []error { |
| if merr == nil { |
| return nil |
| } |
| return merr.errors |
| } |
| |
| // As attempts to find the first error in the error list that matches the type |
| // of the value that target points to. |
| // |
| // This function allows errors.As to traverse the values stored on the |
| // multierr error. |
| func (merr *multiError) As(target interface{}) bool { |
| for _, err := range merr.Errors() { |
| if errors.As(err, target) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // Is attempts to match the provided error against errors in the error list. |
| // |
| // This function allows errors.Is to traverse the values stored on the |
| // multierr error. |
| func (merr *multiError) Is(target error) bool { |
| for _, err := range merr.Errors() { |
| if errors.Is(err, target) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (merr *multiError) Error() string { |
| if merr == nil { |
| return "" |
| } |
| |
| buff := _bufferPool.Get().(*bytes.Buffer) |
| buff.Reset() |
| |
| merr.writeSingleline(buff) |
| |
| result := buff.String() |
| _bufferPool.Put(buff) |
| return result |
| } |
| |
| func (merr *multiError) Format(f fmt.State, c rune) { |
| if c == 'v' && f.Flag('+') { |
| merr.writeMultiline(f) |
| } else { |
| merr.writeSingleline(f) |
| } |
| } |
| |
| func (merr *multiError) writeSingleline(w io.Writer) { |
| first := true |
| for _, item := range merr.errors { |
| if first { |
| first = false |
| } else { |
| w.Write(_singlelineSeparator) |
| } |
| io.WriteString(w, item.Error()) |
| } |
| } |
| |
| func (merr *multiError) writeMultiline(w io.Writer) { |
| w.Write(_multilinePrefix) |
| for _, item := range merr.errors { |
| w.Write(_multilineSeparator) |
| writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item)) |
| } |
| } |
| |
| // Writes s to the writer with the given prefix added before each line after |
| // the first. |
| func writePrefixLine(w io.Writer, prefix []byte, s string) { |
| first := true |
| for len(s) > 0 { |
| if first { |
| first = false |
| } else { |
| w.Write(prefix) |
| } |
| |
| idx := strings.IndexByte(s, '\n') |
| if idx < 0 { |
| idx = len(s) - 1 |
| } |
| |
| io.WriteString(w, s[:idx+1]) |
| s = s[idx+1:] |
| } |
| } |
| |
| type inspectResult struct { |
| // Number of top-level non-nil errors |
| Count int |
| |
| // Total number of errors including multiErrors |
| Capacity int |
| |
| // Index of the first non-nil error in the list. Value is meaningless if |
| // Count is zero. |
| FirstErrorIdx int |
| |
| // Whether the list contains at least one multiError |
| ContainsMultiError bool |
| } |
| |
| // Inspects the given slice of errors so that we can efficiently allocate |
| // space for it. |
| func inspect(errors []error) (res inspectResult) { |
| first := true |
| for i, err := range errors { |
| if err == nil { |
| continue |
| } |
| |
| res.Count++ |
| if first { |
| first = false |
| res.FirstErrorIdx = i |
| } |
| |
| if merr, ok := err.(*multiError); ok { |
| res.Capacity += len(merr.errors) |
| res.ContainsMultiError = true |
| } else { |
| res.Capacity++ |
| } |
| } |
| return |
| } |
| |
| // fromSlice converts the given list of errors into a single error. |
| func fromSlice(errors []error) error { |
| res := inspect(errors) |
| switch res.Count { |
| case 0: |
| return nil |
| case 1: |
| // only one non-nil entry |
| return errors[res.FirstErrorIdx] |
| case len(errors): |
| if !res.ContainsMultiError { |
| // already flat |
| return &multiError{errors: errors} |
| } |
| } |
| |
| nonNilErrs := make([]error, 0, res.Capacity) |
| for _, err := range errors[res.FirstErrorIdx:] { |
| if err == nil { |
| continue |
| } |
| |
| if nested, ok := err.(*multiError); ok { |
| nonNilErrs = append(nonNilErrs, nested.errors...) |
| } else { |
| nonNilErrs = append(nonNilErrs, err) |
| } |
| } |
| |
| return &multiError{errors: nonNilErrs} |
| } |
| |
| // Combine combines the passed errors into a single error. |
| // |
| // If zero arguments were passed or if all items are nil, a nil error is |
| // returned. |
| // |
| // Combine(nil, nil) // == nil |
| // |
| // If only a single error was passed, it is returned as-is. |
| // |
| // Combine(err) // == err |
| // |
| // Combine skips over nil arguments so this function may be used to combine |
| // together errors from operations that fail independently of each other. |
| // |
| // multierr.Combine( |
| // reader.Close(), |
| // writer.Close(), |
| // pipe.Close(), |
| // ) |
| // |
| // If any of the passed errors is a multierr error, it will be flattened along |
| // with the other errors. |
| // |
| // multierr.Combine(multierr.Combine(err1, err2), err3) |
| // // is the same as |
| // multierr.Combine(err1, err2, err3) |
| // |
| // The returned error formats into a readable multi-line error message if |
| // formatted with %+v. |
| // |
| // fmt.Sprintf("%+v", multierr.Combine(err1, err2)) |
| func Combine(errors ...error) error { |
| return fromSlice(errors) |
| } |
| |
| // Append appends the given errors together. Either value may be nil. |
| // |
| // This function is a specialization of Combine for the common case where |
| // there are only two errors. |
| // |
| // err = multierr.Append(reader.Close(), writer.Close()) |
| // |
| // The following pattern may also be used to record failure of deferred |
| // operations without losing information about the original error. |
| // |
| // func doSomething(..) (err error) { |
| // f := acquireResource() |
| // defer func() { |
| // err = multierr.Append(err, f.Close()) |
| // }() |
| func Append(left error, right error) error { |
| switch { |
| case left == nil: |
| return right |
| case right == nil: |
| return left |
| } |
| |
| if _, ok := right.(*multiError); !ok { |
| if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) { |
| // Common case where the error on the left is constantly being |
| // appended to. |
| errs := append(l.errors, right) |
| return &multiError{errors: errs} |
| } else if !ok { |
| // Both errors are single errors. |
| return &multiError{errors: []error{left, right}} |
| } |
| } |
| |
| // Either right or both, left and right, are multiErrors. Rely on usual |
| // expensive logic. |
| errors := [2]error{left, right} |
| return fromSlice(errors[0:]) |
| } |
| |
| // AppendInto appends an error into the destination of an error pointer and |
| // returns whether the error being appended was non-nil. |
| // |
| // var err error |
| // multierr.AppendInto(&err, r.Close()) |
| // multierr.AppendInto(&err, w.Close()) |
| // |
| // The above is equivalent to, |
| // |
| // err := multierr.Append(r.Close(), w.Close()) |
| // |
| // As AppendInto reports whether the provided error was non-nil, it may be |
| // used to build a multierr error in a loop more ergonomically. For example: |
| // |
| // var err error |
| // for line := range lines { |
| // var item Item |
| // if multierr.AppendInto(&err, parse(line, &item)) { |
| // continue |
| // } |
| // items = append(items, item) |
| // } |
| // |
| // Compare this with a version that relies solely on Append: |
| // |
| // var err error |
| // for line := range lines { |
| // var item Item |
| // if parseErr := parse(line, &item); parseErr != nil { |
| // err = multierr.Append(err, parseErr) |
| // continue |
| // } |
| // items = append(items, item) |
| // } |
| func AppendInto(into *error, err error) (errored bool) { |
| if into == nil { |
| // We panic if 'into' is nil. This is not documented above |
| // because suggesting that the pointer must be non-nil may |
| // confuse users into thinking that the error that it points |
| // to must be non-nil. |
| panic("misuse of multierr.AppendInto: into pointer must not be nil") |
| } |
| |
| if err == nil { |
| return false |
| } |
| *into = Append(*into, err) |
| return true |
| } |
| |
| // Invoker is an operation that may fail with an error. Use it with |
| // AppendInvoke to append the result of calling the function into an error. |
| // This allows you to conveniently defer capture of failing operations. |
| // |
| // See also, Close and Invoke. |
| type Invoker interface { |
| Invoke() error |
| } |
| |
| // Invoke wraps a function which may fail with an error to match the Invoker |
| // interface. Use it to supply functions matching this signature to |
| // AppendInvoke. |
| // |
| // For example, |
| // |
| // func processReader(r io.Reader) (err error) { |
| // scanner := bufio.NewScanner(r) |
| // defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) |
| // for scanner.Scan() { |
| // // ... |
| // } |
| // // ... |
| // } |
| // |
| // In this example, the following line will construct the Invoker right away, |
| // but defer the invocation of scanner.Err() until the function returns. |
| // |
| // defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) |
| type Invoke func() error |
| |
| // Invoke calls the supplied function and returns its result. |
| func (i Invoke) Invoke() error { return i() } |
| |
| // Close builds an Invoker that closes the provided io.Closer. Use it with |
| // AppendInvoke to close io.Closers and append their results into an error. |
| // |
| // For example, |
| // |
| // func processFile(path string) (err error) { |
| // f, err := os.Open(path) |
| // if err != nil { |
| // return err |
| // } |
| // defer multierr.AppendInvoke(&err, multierr.Close(f)) |
| // return processReader(f) |
| // } |
| // |
| // In this example, multierr.Close will construct the Invoker right away, but |
| // defer the invocation of f.Close until the function returns. |
| // |
| // defer multierr.AppendInvoke(&err, multierr.Close(f)) |
| func Close(closer io.Closer) Invoker { |
| return Invoke(closer.Close) |
| } |
| |
| // AppendInvoke appends the result of calling the given Invoker into the |
| // provided error pointer. Use it with named returns to safely defer |
| // invocation of fallible operations until a function returns, and capture the |
| // resulting errors. |
| // |
| // func doSomething(...) (err error) { |
| // // ... |
| // f, err := openFile(..) |
| // if err != nil { |
| // return err |
| // } |
| // |
| // // multierr will call f.Close() when this function returns and |
| // // if the operation fails, its append its error into the |
| // // returned error. |
| // defer multierr.AppendInvoke(&err, multierr.Close(f)) |
| // |
| // scanner := bufio.NewScanner(f) |
| // // Similarly, this scheduled scanner.Err to be called and |
| // // inspected when the function returns and append its error |
| // // into the returned error. |
| // defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) |
| // |
| // // ... |
| // } |
| // |
| // Without defer, AppendInvoke behaves exactly like AppendInto. |
| // |
| // err := // ... |
| // multierr.AppendInvoke(&err, mutltierr.Invoke(foo)) |
| // |
| // // ...is roughly equivalent to... |
| // |
| // err := // ... |
| // multierr.AppendInto(&err, foo()) |
| // |
| // The advantage of the indirection introduced by Invoker is to make it easy |
| // to defer the invocation of a function. Without this indirection, the |
| // invoked function will be evaluated at the time of the defer block rather |
| // than when the function returns. |
| // |
| // // BAD: This is likely not what the caller intended. This will evaluate |
| // // foo() right away and append its result into the error when the |
| // // function returns. |
| // defer multierr.AppendInto(&err, foo()) |
| // |
| // // GOOD: This will defer invocation of foo unutil the function returns. |
| // defer multierr.AppendInvoke(&err, multierr.Invoke(foo)) |
| // |
| // multierr provides a few Invoker implementations out of the box for |
| // convenience. See Invoker for more information. |
| func AppendInvoke(into *error, invoker Invoker) { |
| AppendInto(into, invoker.Invoke()) |
| } |