github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/errors/errors.go (about)

     1  // Copyright (c) 2016 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 errors provides utilities for working with different types errors.
    22  package errors
    23  
    24  import (
    25  	"bytes"
    26  	"errors"
    27  	"fmt"
    28  )
    29  
    30  // FirstError returns the first non nil error.
    31  func FirstError(errs ...error) error {
    32  	for i := range errs {
    33  		if errs[i] != nil {
    34  			return errs[i]
    35  		}
    36  	}
    37  	return nil
    38  }
    39  
    40  type containedError struct {
    41  	inner error
    42  }
    43  
    44  func (e containedError) Error() string {
    45  	return e.inner.Error()
    46  }
    47  
    48  func (e containedError) InnerError() error {
    49  	return e.inner
    50  }
    51  
    52  // ContainedError is an error with a contained error.
    53  type ContainedError interface {
    54  	InnerError() error
    55  }
    56  
    57  // InnerError returns the packaged inner error if this is an error that
    58  // contains another.
    59  func InnerError(err error) error {
    60  	contained, ok := err.(ContainedError)
    61  	if !ok {
    62  		return nil
    63  	}
    64  	return contained.InnerError()
    65  }
    66  
    67  type renamedError struct {
    68  	containedError
    69  	renamed error
    70  }
    71  
    72  // NewRenamedError returns a new error that packages an inner error with
    73  // a renamed error.
    74  func NewRenamedError(inner, renamed error) error {
    75  	return renamedError{containedError{inner}, renamed}
    76  }
    77  
    78  func (e renamedError) Error() string {
    79  	return e.renamed.Error()
    80  }
    81  
    82  func (e renamedError) InnerError() error {
    83  	return e.inner
    84  }
    85  
    86  type invalidParamsError struct {
    87  	containedError
    88  }
    89  
    90  // Wrap wraps an error with a message but preserves the type of the error.
    91  func Wrap(err error, msg string) error {
    92  	renamed := errors.New(msg + ": " + err.Error())
    93  	return NewRenamedError(err, renamed)
    94  }
    95  
    96  // Wrapf formats according to a format specifier and uses that string to
    97  // wrap an error while still preserving the type of the error.
    98  func Wrapf(err error, format string, args ...interface{}) error {
    99  	msg := fmt.Sprintf(format, args...)
   100  	return Wrap(err, msg)
   101  }
   102  
   103  // NewInvalidParamsError creates a new invalid params error
   104  func NewInvalidParamsError(inner error) error {
   105  	return invalidParamsError{containedError{inner}}
   106  }
   107  
   108  func (e invalidParamsError) Error() string {
   109  	return e.inner.Error()
   110  }
   111  
   112  func (e invalidParamsError) InnerError() error {
   113  	return e.inner
   114  }
   115  
   116  // IsInvalidParams returns true if this is an invalid params error.
   117  func IsInvalidParams(err error) bool {
   118  	return GetInnerInvalidParamsError(err) != nil
   119  }
   120  
   121  // GetInnerInvalidParamsError returns an inner invalid params error
   122  // if contained by this error, nil otherwise.
   123  func GetInnerInvalidParamsError(err error) error {
   124  	for err != nil {
   125  		if _, ok := err.(invalidParamsError); ok {
   126  			return InnerError(err)
   127  		}
   128  		// nolint:errorlint
   129  		if multiErr, ok := err.(MultiError); ok {
   130  			for _, e := range multiErr.Errors() {
   131  				if inner := GetInnerInvalidParamsError(e); err != nil {
   132  					return inner
   133  				}
   134  			}
   135  		}
   136  		err = InnerError(err)
   137  	}
   138  	return nil
   139  }
   140  
   141  type resourceExhaustedError struct {
   142  	containedError
   143  }
   144  
   145  // NewResourceExhaustedError creates a new resource exhausted error
   146  func NewResourceExhaustedError(inner error) error {
   147  	return resourceExhaustedError{containedError{inner}}
   148  }
   149  
   150  func (e resourceExhaustedError) Error() string {
   151  	return e.inner.Error()
   152  }
   153  
   154  func (e resourceExhaustedError) InnerError() error {
   155  	return e.inner
   156  }
   157  
   158  // IsResourceExhausted returns true if this is a resource exhausted error.
   159  func IsResourceExhausted(err error) bool {
   160  	return GetInnerResourceExhaustedError(err) != nil
   161  }
   162  
   163  // GetInnerResourceExhaustedError returns an inner resource exhausted error
   164  // if contained by this error, nil otherwise.
   165  func GetInnerResourceExhaustedError(err error) error {
   166  	for err != nil {
   167  		// nolint:errorlint
   168  		if _, ok := err.(resourceExhaustedError); ok {
   169  			return InnerError(err)
   170  		}
   171  		// nolint:errorlint
   172  		if multiErr, ok := err.(MultiError); ok {
   173  			for _, e := range multiErr.Errors() {
   174  				if inner := GetInnerResourceExhaustedError(e); err != nil {
   175  					return inner
   176  				}
   177  			}
   178  		}
   179  		err = InnerError(err)
   180  	}
   181  	return nil
   182  }
   183  
   184  // Is checks if the error is or contains the corresponding target error.
   185  // It's intended to mimic the errors.Is functionality, but also consider xerrors' MultiError / InnerError
   186  // wrapping functionality.
   187  func Is(err, target error) bool {
   188  	for err != nil {
   189  		if errors.Is(err, target) {
   190  			return true
   191  		}
   192  
   193  		// nolint:errorlint
   194  		if multiErr, ok := err.(MultiError); ok {
   195  			for _, e := range multiErr.Errors() {
   196  				if Is(e, target) {
   197  					return true
   198  				}
   199  			}
   200  		}
   201  
   202  		err = InnerError(err)
   203  	}
   204  	return false
   205  }
   206  
   207  type retryableError struct {
   208  	containedError
   209  }
   210  
   211  // NewRetryableError creates a new retryable error.
   212  func NewRetryableError(inner error) error {
   213  	return retryableError{containedError{inner}}
   214  }
   215  
   216  func (e retryableError) Error() string {
   217  	return e.inner.Error()
   218  }
   219  
   220  func (e retryableError) InnerError() error {
   221  	return e.inner
   222  }
   223  
   224  // IsRetryableError returns true if this is a retryable error.
   225  func IsRetryableError(err error) bool {
   226  	return GetInnerRetryableError(err) != nil
   227  }
   228  
   229  // GetInnerRetryableError returns an inner retryable error
   230  // if contained by this error, nil otherwise.
   231  func GetInnerRetryableError(err error) error {
   232  	for err != nil {
   233  		if _, ok := err.(retryableError); ok {
   234  			return InnerError(err)
   235  		}
   236  		// nolint:errorlint
   237  		if multiErr, ok := err.(MultiError); ok {
   238  			for _, e := range multiErr.Errors() {
   239  				if inner := GetInnerRetryableError(e); err != nil {
   240  					return inner
   241  				}
   242  			}
   243  		}
   244  		err = InnerError(err)
   245  	}
   246  	return nil
   247  }
   248  
   249  type nonRetryableError struct {
   250  	containedError
   251  }
   252  
   253  // NewNonRetryableError creates a new non-retryable error.
   254  func NewNonRetryableError(inner error) error {
   255  	return nonRetryableError{containedError{inner}}
   256  }
   257  
   258  func (e nonRetryableError) Error() string {
   259  	return e.inner.Error()
   260  }
   261  
   262  func (e nonRetryableError) InnerError() error {
   263  	return e.inner
   264  }
   265  
   266  // IsNonRetryableError returns true if this is a non-retryable error.
   267  func IsNonRetryableError(err error) bool {
   268  	return GetInnerNonRetryableError(err) != nil
   269  }
   270  
   271  // GetInnerNonRetryableError returns an inner non-retryable error
   272  // if contained by this error, nil otherwise.
   273  func GetInnerNonRetryableError(err error) error {
   274  	for err != nil {
   275  		if _, ok := err.(nonRetryableError); ok {
   276  			return InnerError(err)
   277  		}
   278  		// nolint:errorlint
   279  		if multiErr, ok := err.(MultiError); ok {
   280  			for _, e := range multiErr.Errors() {
   281  				if inner := GetInnerNonRetryableError(e); err != nil {
   282  					return inner
   283  				}
   284  			}
   285  		}
   286  		err = InnerError(err)
   287  	}
   288  	return nil
   289  }
   290  
   291  // IsMultiError returns true if this is a multi-error error.
   292  func IsMultiError(err error) bool {
   293  	_, ok := GetInnerMultiError(err)
   294  	return ok
   295  }
   296  
   297  // GetInnerMultiError returns an inner multi-error error
   298  // if contained by this error, nil otherwise.
   299  func GetInnerMultiError(err error) (MultiError, bool) {
   300  	for err != nil {
   301  		if v, ok := err.(MultiError); ok {
   302  			return v, true
   303  		}
   304  		err = InnerError(err)
   305  	}
   306  	return MultiError{}, false
   307  }
   308  
   309  // MultiError is an immutable error that packages a list of errors.
   310  //
   311  // TODO(xichen): we may want to limit the number of errors included.
   312  type MultiError struct {
   313  	err    error // optimization for single error case
   314  	errors []error
   315  }
   316  
   317  // NewMultiError creates a new MultiError object.
   318  func NewMultiError() MultiError {
   319  	return MultiError{}
   320  }
   321  
   322  // Empty returns true if the MultiError has no errors.
   323  func (e MultiError) Empty() bool {
   324  	return e.err == nil
   325  }
   326  
   327  func (e MultiError) Error() string {
   328  	if e.err == nil {
   329  		return ""
   330  	}
   331  	if len(e.errors) == 0 {
   332  		return e.err.Error()
   333  	}
   334  	var b bytes.Buffer
   335  	for i := range e.errors {
   336  		b.WriteString(e.errors[i].Error())
   337  		b.WriteString("\n")
   338  	}
   339  	b.WriteString(e.err.Error())
   340  	return b.String()
   341  }
   342  
   343  // Errors returns all the errors to inspect individually.
   344  func (e MultiError) Errors() []error {
   345  	if e.err == nil {
   346  		return nil // No errors
   347  	}
   348  	// Need to prepend the first error to result
   349  	// since we avoid allocating array if we don't need it
   350  	// when we accumulate the first error
   351  	result := make([]error, 1+len(e.errors))
   352  	result[0] = e.err
   353  	copy(result[1:], e.errors)
   354  	return result
   355  }
   356  
   357  // Contains returns true if any of the errors match the provided error using the Is check.
   358  func (e MultiError) Contains(err error) bool {
   359  	if errors.Is(e.err, err) {
   360  		return true
   361  	}
   362  	for _, e := range e.errors {
   363  		if errors.Is(e, err) {
   364  			return true
   365  		}
   366  	}
   367  	return false
   368  }
   369  
   370  // Add adds an error returns a new MultiError object.
   371  func (e MultiError) Add(err error) MultiError {
   372  	if err == nil {
   373  		return e
   374  	}
   375  	me := e
   376  	if me.err == nil {
   377  		me.err = err
   378  		return me
   379  	}
   380  	me.errors = append(me.errors, me.err)
   381  	me.err = err
   382  	return me
   383  }
   384  
   385  // FinalError returns all concatenated error messages if any.
   386  func (e MultiError) FinalError() error {
   387  	if e.err == nil {
   388  		return nil
   389  	}
   390  	return e
   391  }
   392  
   393  // LastError returns the last received error if any.
   394  func (e MultiError) LastError() error {
   395  	if e.err == nil {
   396  		return nil
   397  	}
   398  	return e.err
   399  }
   400  
   401  // NumErrors returns the total number of errors.
   402  func (e MultiError) NumErrors() int {
   403  	if e.err == nil {
   404  		return 0
   405  	}
   406  	return len(e.errors) + 1
   407  }
   408  
   409  // Errors is a slice of errors that itself is an error too.
   410  type Errors []error
   411  
   412  // Error implements error.
   413  func (e Errors) Error() string {
   414  	buf := bytes.NewBuffer(nil)
   415  	buf.WriteString("[")
   416  	for i, err := range e {
   417  		if err == nil {
   418  			buf.WriteString("<nil>")
   419  		} else {
   420  			buf.WriteString("<")
   421  			buf.WriteString(err.Error())
   422  			buf.WriteString(">")
   423  		}
   424  		if i < len(e)-1 {
   425  			buf.WriteString(", ")
   426  		}
   427  	}
   428  	buf.WriteString("]")
   429  	return buf.String()
   430  }