...

Source file src/go.uber.org/multierr/error.go

Documentation: go.uber.org/multierr

     1  // Copyright (c) 2017-2023 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  // Package multierr allows combining one or more errors together.
    22  //
    23  // # Overview
    24  //
    25  // Errors can be combined with the use of the Combine function.
    26  //
    27  //	multierr.Combine(
    28  //		reader.Close(),
    29  //		writer.Close(),
    30  //		conn.Close(),
    31  //	)
    32  //
    33  // If only two errors are being combined, the Append function may be used
    34  // instead.
    35  //
    36  //	err = multierr.Append(reader.Close(), writer.Close())
    37  //
    38  // The underlying list of errors for a returned error object may be retrieved
    39  // with the Errors function.
    40  //
    41  //	errors := multierr.Errors(err)
    42  //	if len(errors) > 0 {
    43  //		fmt.Println("The following errors occurred:", errors)
    44  //	}
    45  //
    46  // # Appending from a loop
    47  //
    48  // You sometimes need to append into an error from a loop.
    49  //
    50  //	var err error
    51  //	for _, item := range items {
    52  //		err = multierr.Append(err, process(item))
    53  //	}
    54  //
    55  // Cases like this may require knowledge of whether an individual instance
    56  // failed. This usually requires introduction of a new variable.
    57  //
    58  //	var err error
    59  //	for _, item := range items {
    60  //		if perr := process(item); perr != nil {
    61  //			log.Warn("skipping item", item)
    62  //			err = multierr.Append(err, perr)
    63  //		}
    64  //	}
    65  //
    66  // multierr includes AppendInto to simplify cases like this.
    67  //
    68  //	var err error
    69  //	for _, item := range items {
    70  //		if multierr.AppendInto(&err, process(item)) {
    71  //			log.Warn("skipping item", item)
    72  //		}
    73  //	}
    74  //
    75  // This will append the error into the err variable, and return true if that
    76  // individual error was non-nil.
    77  //
    78  // See [AppendInto] for more information.
    79  //
    80  // # Deferred Functions
    81  //
    82  // Go makes it possible to modify the return value of a function in a defer
    83  // block if the function was using named returns. This makes it possible to
    84  // record resource cleanup failures from deferred blocks.
    85  //
    86  //	func sendRequest(req Request) (err error) {
    87  //		conn, err := openConnection()
    88  //		if err != nil {
    89  //			return err
    90  //		}
    91  //		defer func() {
    92  //			err = multierr.Append(err, conn.Close())
    93  //		}()
    94  //		// ...
    95  //	}
    96  //
    97  // multierr provides the Invoker type and AppendInvoke function to make cases
    98  // like the above simpler and obviate the need for a closure. The following is
    99  // roughly equivalent to the example above.
   100  //
   101  //	func sendRequest(req Request) (err error) {
   102  //		conn, err := openConnection()
   103  //		if err != nil {
   104  //			return err
   105  //		}
   106  //		defer multierr.AppendInvoke(&err, multierr.Close(conn))
   107  //		// ...
   108  //	}
   109  //
   110  // See [AppendInvoke] and [Invoker] for more information.
   111  //
   112  // NOTE: If you're modifying an error from inside a defer, you MUST use a named
   113  // return value for that function.
   114  //
   115  // # Advanced Usage
   116  //
   117  // Errors returned by Combine and Append MAY implement the following
   118  // interface.
   119  //
   120  //	type errorGroup interface {
   121  //		// Returns a slice containing the underlying list of errors.
   122  //		//
   123  //		// This slice MUST NOT be modified by the caller.
   124  //		Errors() []error
   125  //	}
   126  //
   127  // Note that if you need access to list of errors behind a multierr error, you
   128  // should prefer using the Errors function. That said, if you need cheap
   129  // read-only access to the underlying errors slice, you can attempt to cast
   130  // the error to this interface. You MUST handle the failure case gracefully
   131  // because errors returned by Combine and Append are not guaranteed to
   132  // implement this interface.
   133  //
   134  //	var errors []error
   135  //	group, ok := err.(errorGroup)
   136  //	if ok {
   137  //		errors = group.Errors()
   138  //	} else {
   139  //		errors = []error{err}
   140  //	}
   141  package multierr // import "go.uber.org/multierr"
   142  
   143  import (
   144  	"bytes"
   145  	"errors"
   146  	"fmt"
   147  	"io"
   148  	"strings"
   149  	"sync"
   150  	"sync/atomic"
   151  )
   152  
   153  var (
   154  	// Separator for single-line error messages.
   155  	_singlelineSeparator = []byte("; ")
   156  
   157  	// Prefix for multi-line messages
   158  	_multilinePrefix = []byte("the following errors occurred:")
   159  
   160  	// Prefix for the first and following lines of an item in a list of
   161  	// multi-line error messages.
   162  	//
   163  	// For example, if a single item is:
   164  	//
   165  	// 	foo
   166  	// 	bar
   167  	//
   168  	// It will become,
   169  	//
   170  	// 	 -  foo
   171  	// 	    bar
   172  	_multilineSeparator = []byte("\n -  ")
   173  	_multilineIndent    = []byte("    ")
   174  )
   175  
   176  // _bufferPool is a pool of bytes.Buffers.
   177  var _bufferPool = sync.Pool{
   178  	New: func() interface{} {
   179  		return &bytes.Buffer{}
   180  	},
   181  }
   182  
   183  type errorGroup interface {
   184  	Errors() []error
   185  }
   186  
   187  // Errors returns a slice containing zero or more errors that the supplied
   188  // error is composed of. If the error is nil, a nil slice is returned.
   189  //
   190  //	err := multierr.Append(r.Close(), w.Close())
   191  //	errors := multierr.Errors(err)
   192  //
   193  // If the error is not composed of other errors, the returned slice contains
   194  // just the error that was passed in.
   195  //
   196  // Callers of this function are free to modify the returned slice.
   197  func Errors(err error) []error {
   198  	return extractErrors(err)
   199  }
   200  
   201  // multiError is an error that holds one or more errors.
   202  //
   203  // An instance of this is guaranteed to be non-empty and flattened. That is,
   204  // none of the errors inside multiError are other multiErrors.
   205  //
   206  // multiError formats to a semi-colon delimited list of error messages with
   207  // %v and with a more readable multi-line format with %+v.
   208  type multiError struct {
   209  	copyNeeded atomic.Bool
   210  	errors     []error
   211  }
   212  
   213  // Errors returns the list of underlying errors.
   214  //
   215  // This slice MUST NOT be modified.
   216  func (merr *multiError) Errors() []error {
   217  	if merr == nil {
   218  		return nil
   219  	}
   220  	return merr.errors
   221  }
   222  
   223  func (merr *multiError) Error() string {
   224  	if merr == nil {
   225  		return ""
   226  	}
   227  
   228  	buff := _bufferPool.Get().(*bytes.Buffer)
   229  	buff.Reset()
   230  
   231  	merr.writeSingleline(buff)
   232  
   233  	result := buff.String()
   234  	_bufferPool.Put(buff)
   235  	return result
   236  }
   237  
   238  // Every compares every error in the given err against the given target error
   239  // using [errors.Is], and returns true only if every comparison returned true.
   240  func Every(err error, target error) bool {
   241  	for _, e := range extractErrors(err) {
   242  		if !errors.Is(e, target) {
   243  			return false
   244  		}
   245  	}
   246  	return true
   247  }
   248  
   249  func (merr *multiError) Format(f fmt.State, c rune) {
   250  	if c == 'v' && f.Flag('+') {
   251  		merr.writeMultiline(f)
   252  	} else {
   253  		merr.writeSingleline(f)
   254  	}
   255  }
   256  
   257  func (merr *multiError) writeSingleline(w io.Writer) {
   258  	first := true
   259  	for _, item := range merr.errors {
   260  		if first {
   261  			first = false
   262  		} else {
   263  			w.Write(_singlelineSeparator)
   264  		}
   265  		io.WriteString(w, item.Error())
   266  	}
   267  }
   268  
   269  func (merr *multiError) writeMultiline(w io.Writer) {
   270  	w.Write(_multilinePrefix)
   271  	for _, item := range merr.errors {
   272  		w.Write(_multilineSeparator)
   273  		writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
   274  	}
   275  }
   276  
   277  // Writes s to the writer with the given prefix added before each line after
   278  // the first.
   279  func writePrefixLine(w io.Writer, prefix []byte, s string) {
   280  	first := true
   281  	for len(s) > 0 {
   282  		if first {
   283  			first = false
   284  		} else {
   285  			w.Write(prefix)
   286  		}
   287  
   288  		idx := strings.IndexByte(s, '\n')
   289  		if idx < 0 {
   290  			idx = len(s) - 1
   291  		}
   292  
   293  		io.WriteString(w, s[:idx+1])
   294  		s = s[idx+1:]
   295  	}
   296  }
   297  
   298  type inspectResult struct {
   299  	// Number of top-level non-nil errors
   300  	Count int
   301  
   302  	// Total number of errors including multiErrors
   303  	Capacity int
   304  
   305  	// Index of the first non-nil error in the list. Value is meaningless if
   306  	// Count is zero.
   307  	FirstErrorIdx int
   308  
   309  	// Whether the list contains at least one multiError
   310  	ContainsMultiError bool
   311  }
   312  
   313  // Inspects the given slice of errors so that we can efficiently allocate
   314  // space for it.
   315  func inspect(errors []error) (res inspectResult) {
   316  	first := true
   317  	for i, err := range errors {
   318  		if err == nil {
   319  			continue
   320  		}
   321  
   322  		res.Count++
   323  		if first {
   324  			first = false
   325  			res.FirstErrorIdx = i
   326  		}
   327  
   328  		if merr, ok := err.(*multiError); ok {
   329  			res.Capacity += len(merr.errors)
   330  			res.ContainsMultiError = true
   331  		} else {
   332  			res.Capacity++
   333  		}
   334  	}
   335  	return
   336  }
   337  
   338  // fromSlice converts the given list of errors into a single error.
   339  func fromSlice(errors []error) error {
   340  	// Don't pay to inspect small slices.
   341  	switch len(errors) {
   342  	case 0:
   343  		return nil
   344  	case 1:
   345  		return errors[0]
   346  	}
   347  
   348  	res := inspect(errors)
   349  	switch res.Count {
   350  	case 0:
   351  		return nil
   352  	case 1:
   353  		// only one non-nil entry
   354  		return errors[res.FirstErrorIdx]
   355  	case len(errors):
   356  		if !res.ContainsMultiError {
   357  			// Error list is flat. Make a copy of it
   358  			// Otherwise "errors" escapes to the heap
   359  			// unconditionally for all other cases.
   360  			// This lets us optimize for the "no errors" case.
   361  			out := append(([]error)(nil), errors...)
   362  			return &multiError{errors: out}
   363  		}
   364  	}
   365  
   366  	nonNilErrs := make([]error, 0, res.Capacity)
   367  	for _, err := range errors[res.FirstErrorIdx:] {
   368  		if err == nil {
   369  			continue
   370  		}
   371  
   372  		if nested, ok := err.(*multiError); ok {
   373  			nonNilErrs = append(nonNilErrs, nested.errors...)
   374  		} else {
   375  			nonNilErrs = append(nonNilErrs, err)
   376  		}
   377  	}
   378  
   379  	return &multiError{errors: nonNilErrs}
   380  }
   381  
   382  // Combine combines the passed errors into a single error.
   383  //
   384  // If zero arguments were passed or if all items are nil, a nil error is
   385  // returned.
   386  //
   387  //	Combine(nil, nil)  // == nil
   388  //
   389  // If only a single error was passed, it is returned as-is.
   390  //
   391  //	Combine(err)  // == err
   392  //
   393  // Combine skips over nil arguments so this function may be used to combine
   394  // together errors from operations that fail independently of each other.
   395  //
   396  //	multierr.Combine(
   397  //		reader.Close(),
   398  //		writer.Close(),
   399  //		pipe.Close(),
   400  //	)
   401  //
   402  // If any of the passed errors is a multierr error, it will be flattened along
   403  // with the other errors.
   404  //
   405  //	multierr.Combine(multierr.Combine(err1, err2), err3)
   406  //	// is the same as
   407  //	multierr.Combine(err1, err2, err3)
   408  //
   409  // The returned error formats into a readable multi-line error message if
   410  // formatted with %+v.
   411  //
   412  //	fmt.Sprintf("%+v", multierr.Combine(err1, err2))
   413  func Combine(errors ...error) error {
   414  	return fromSlice(errors)
   415  }
   416  
   417  // Append appends the given errors together. Either value may be nil.
   418  //
   419  // This function is a specialization of Combine for the common case where
   420  // there are only two errors.
   421  //
   422  //	err = multierr.Append(reader.Close(), writer.Close())
   423  //
   424  // The following pattern may also be used to record failure of deferred
   425  // operations without losing information about the original error.
   426  //
   427  //	func doSomething(..) (err error) {
   428  //		f := acquireResource()
   429  //		defer func() {
   430  //			err = multierr.Append(err, f.Close())
   431  //		}()
   432  //
   433  // Note that the variable MUST be a named return to append an error to it from
   434  // the defer statement. See also [AppendInvoke].
   435  func Append(left error, right error) error {
   436  	switch {
   437  	case left == nil:
   438  		return right
   439  	case right == nil:
   440  		return left
   441  	}
   442  
   443  	if _, ok := right.(*multiError); !ok {
   444  		if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
   445  			// Common case where the error on the left is constantly being
   446  			// appended to.
   447  			errs := append(l.errors, right)
   448  			return &multiError{errors: errs}
   449  		} else if !ok {
   450  			// Both errors are single errors.
   451  			return &multiError{errors: []error{left, right}}
   452  		}
   453  	}
   454  
   455  	// Either right or both, left and right, are multiErrors. Rely on usual
   456  	// expensive logic.
   457  	errors := [2]error{left, right}
   458  	return fromSlice(errors[0:])
   459  }
   460  
   461  // AppendInto appends an error into the destination of an error pointer and
   462  // returns whether the error being appended was non-nil.
   463  //
   464  //	var err error
   465  //	multierr.AppendInto(&err, r.Close())
   466  //	multierr.AppendInto(&err, w.Close())
   467  //
   468  // The above is equivalent to,
   469  //
   470  //	err := multierr.Append(r.Close(), w.Close())
   471  //
   472  // As AppendInto reports whether the provided error was non-nil, it may be
   473  // used to build a multierr error in a loop more ergonomically. For example:
   474  //
   475  //	var err error
   476  //	for line := range lines {
   477  //		var item Item
   478  //		if multierr.AppendInto(&err, parse(line, &item)) {
   479  //			continue
   480  //		}
   481  //		items = append(items, item)
   482  //	}
   483  //
   484  // Compare this with a version that relies solely on Append:
   485  //
   486  //	var err error
   487  //	for line := range lines {
   488  //		var item Item
   489  //		if parseErr := parse(line, &item); parseErr != nil {
   490  //			err = multierr.Append(err, parseErr)
   491  //			continue
   492  //		}
   493  //		items = append(items, item)
   494  //	}
   495  func AppendInto(into *error, err error) (errored bool) {
   496  	if into == nil {
   497  		// We panic if 'into' is nil. This is not documented above
   498  		// because suggesting that the pointer must be non-nil may
   499  		// confuse users into thinking that the error that it points
   500  		// to must be non-nil.
   501  		panic("misuse of multierr.AppendInto: into pointer must not be nil")
   502  	}
   503  
   504  	if err == nil {
   505  		return false
   506  	}
   507  	*into = Append(*into, err)
   508  	return true
   509  }
   510  
   511  // Invoker is an operation that may fail with an error. Use it with
   512  // AppendInvoke to append the result of calling the function into an error.
   513  // This allows you to conveniently defer capture of failing operations.
   514  //
   515  // See also, [Close] and [Invoke].
   516  type Invoker interface {
   517  	Invoke() error
   518  }
   519  
   520  // Invoke wraps a function which may fail with an error to match the Invoker
   521  // interface. Use it to supply functions matching this signature to
   522  // AppendInvoke.
   523  //
   524  // For example,
   525  //
   526  //	func processReader(r io.Reader) (err error) {
   527  //		scanner := bufio.NewScanner(r)
   528  //		defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
   529  //		for scanner.Scan() {
   530  //			// ...
   531  //		}
   532  //		// ...
   533  //	}
   534  //
   535  // In this example, the following line will construct the Invoker right away,
   536  // but defer the invocation of scanner.Err() until the function returns.
   537  //
   538  //	defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
   539  //
   540  // Note that the error you're appending to from the defer statement MUST be a
   541  // named return.
   542  type Invoke func() error
   543  
   544  // Invoke calls the supplied function and returns its result.
   545  func (i Invoke) Invoke() error { return i() }
   546  
   547  // Close builds an Invoker that closes the provided io.Closer. Use it with
   548  // AppendInvoke to close io.Closers and append their results into an error.
   549  //
   550  // For example,
   551  //
   552  //	func processFile(path string) (err error) {
   553  //		f, err := os.Open(path)
   554  //		if err != nil {
   555  //			return err
   556  //		}
   557  //		defer multierr.AppendInvoke(&err, multierr.Close(f))
   558  //		return processReader(f)
   559  //	}
   560  //
   561  // In this example, multierr.Close will construct the Invoker right away, but
   562  // defer the invocation of f.Close until the function returns.
   563  //
   564  //	defer multierr.AppendInvoke(&err, multierr.Close(f))
   565  //
   566  // Note that the error you're appending to from the defer statement MUST be a
   567  // named return.
   568  func Close(closer io.Closer) Invoker {
   569  	return Invoke(closer.Close)
   570  }
   571  
   572  // AppendInvoke appends the result of calling the given Invoker into the
   573  // provided error pointer. Use it with named returns to safely defer
   574  // invocation of fallible operations until a function returns, and capture the
   575  // resulting errors.
   576  //
   577  //	func doSomething(...) (err error) {
   578  //		// ...
   579  //		f, err := openFile(..)
   580  //		if err != nil {
   581  //			return err
   582  //		}
   583  //
   584  //		// multierr will call f.Close() when this function returns and
   585  //		// if the operation fails, its append its error into the
   586  //		// returned error.
   587  //		defer multierr.AppendInvoke(&err, multierr.Close(f))
   588  //
   589  //		scanner := bufio.NewScanner(f)
   590  //		// Similarly, this scheduled scanner.Err to be called and
   591  //		// inspected when the function returns and append its error
   592  //		// into the returned error.
   593  //		defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
   594  //
   595  //		// ...
   596  //	}
   597  //
   598  // NOTE: If used with a defer, the error variable MUST be a named return.
   599  //
   600  // Without defer, AppendInvoke behaves exactly like AppendInto.
   601  //
   602  //	err := // ...
   603  //	multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
   604  //
   605  //	// ...is roughly equivalent to...
   606  //
   607  //	err := // ...
   608  //	multierr.AppendInto(&err, foo())
   609  //
   610  // The advantage of the indirection introduced by Invoker is to make it easy
   611  // to defer the invocation of a function. Without this indirection, the
   612  // invoked function will be evaluated at the time of the defer block rather
   613  // than when the function returns.
   614  //
   615  //	// BAD: This is likely not what the caller intended. This will evaluate
   616  //	// foo() right away and append its result into the error when the
   617  //	// function returns.
   618  //	defer multierr.AppendInto(&err, foo())
   619  //
   620  //	// GOOD: This will defer invocation of foo unutil the function returns.
   621  //	defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
   622  //
   623  // multierr provides a few Invoker implementations out of the box for
   624  // convenience. See [Invoker] for more information.
   625  func AppendInvoke(into *error, invoker Invoker) {
   626  	AppendInto(into, invoker.Invoke())
   627  }
   628  
   629  // AppendFunc is a shorthand for [AppendInvoke].
   630  // It allows using function or method value directly
   631  // without having to wrap it into an [Invoker] interface.
   632  //
   633  //	func doSomething(...) (err error) {
   634  //		w, err := startWorker(...)
   635  //		if err != nil {
   636  //			return err
   637  //		}
   638  //
   639  //		// multierr will call w.Stop() when this function returns and
   640  //		// if the operation fails, it appends its error into the
   641  //		// returned error.
   642  //		defer multierr.AppendFunc(&err, w.Stop)
   643  //	}
   644  func AppendFunc(into *error, fn func() error) {
   645  	AppendInvoke(into, Invoke(fn))
   646  }
   647  

View as plain text