github.com/thanos-io/thanos@v0.32.5/pkg/errors/errors.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  //nolint
     5  // The idea of writing errors package in thanos is highly motivated from the Tast project of Chromium OS Authors. However, instead of
     6  // copying the package, we end up writing our own simplified logic borrowing some ideas from the errors and github.com/pkg/errors.
     7  // A big thanks to all of them.
     8  
     9  // Package errors provides basic utilities to manipulate errors with a useful stacktrace. It combines the
    10  // benefits of errors.New and fmt.Errorf world into a single package.
    11  package errors
    12  
    13  import (
    14  	//lint:ignore faillint Custom errors package needs to import standard library errors.
    15  	"errors"
    16  	"fmt"
    17  	"strings"
    18  )
    19  
    20  // base is the fundamental struct that implements the error interface and the acts as the backbone of this errors package.
    21  type base struct {
    22  	// info contains the error message passed through calls like errors.Wrap, errors.New.
    23  	info string
    24  	// stacktrace stores information about the program counters - i.e. where this error was generated.
    25  	stack stacktrace
    26  	// err is the actual error which is being wrapped with a stacktrace and message information.
    27  	err error
    28  }
    29  
    30  // Error implements the error interface.
    31  func (b *base) Error() string {
    32  	if b.err != nil {
    33  		return fmt.Sprintf("%s: %s", b.info, b.err.Error())
    34  	}
    35  	return b.info
    36  }
    37  
    38  // Unwrap implements the error Unwrap interface.
    39  func (b *base) Unwrap() error {
    40  	return b.err
    41  }
    42  
    43  // Format implements the fmt.Formatter interface to support the formatting of an error chain with "%+v" verb.
    44  // Whenever error is printed with %+v format verb, stacktrace info gets dumped to the output.
    45  func (b *base) Format(s fmt.State, verb rune) {
    46  	if verb == 'v' && s.Flag('+') {
    47  		s.Write([]byte(formatErrorChain(b)))
    48  		return
    49  	}
    50  
    51  	s.Write([]byte(b.Error()))
    52  }
    53  
    54  // Newf formats according to a format specifier and returns a new error with a stacktrace
    55  // with recent call frames. Each call to New returns a distinct error value even if the text is
    56  // identical. An alternative of the errors.New function.
    57  //
    58  // If no args have been passed, it is same as `New` function without formatting. Character like
    59  // '%' still has to be escaped in that scenario.
    60  func Newf(format string, args ...interface{}) error {
    61  	return &base{
    62  		info:  fmt.Sprintf(format, args...),
    63  		stack: newStackTrace(),
    64  		err:   nil,
    65  	}
    66  }
    67  
    68  // Wrapf returns a new error by formatting the error message with the supplied format specifier
    69  // and wrapping another error with a stacktrace containing recent call frames.
    70  //
    71  // If cause is nil, this is the same as fmt.Errorf. If no args have been passed, it is same as `Wrap`
    72  // function without formatting. Character like '%' still has to be escaped in that scenario.
    73  func Wrapf(cause error, format string, args ...interface{}) error {
    74  	return &base{
    75  		info:  fmt.Sprintf(format, args...),
    76  		stack: newStackTrace(),
    77  		err:   cause,
    78  	}
    79  }
    80  
    81  // Cause returns the result of repeatedly calling the Unwrap method on err, if err's
    82  // type implements an Unwrap method. Otherwise, Cause returns the last encountered error.
    83  // The difference between Unwrap and Cause is the first one performs unwrapping of one level
    84  // but Cause returns the last err (whether it's nil or not) where it failed to assert
    85  // the interface containing the Unwrap method.
    86  // This is a replacement of errors.Cause without the causer interface from pkg/errors which
    87  // actually can be sufficed through the errors.Is function. But considering some use cases
    88  // where we need to peel off all the external layers applied through errors.Wrap family,
    89  // it is useful ( where external SDK doesn't use errors.Is internally).
    90  func Cause(err error) error {
    91  	for err != nil {
    92  		e, ok := err.(interface {
    93  			Unwrap() error
    94  		})
    95  		if !ok {
    96  			return err
    97  		}
    98  		err = e.Unwrap()
    99  	}
   100  	return nil
   101  }
   102  
   103  // formatErrorChain formats an error chain.
   104  func formatErrorChain(err error) string {
   105  	var buf strings.Builder
   106  	for err != nil {
   107  		if e, ok := err.(*base); ok {
   108  			buf.WriteString(fmt.Sprintf("%s\n%v", e.info, e.stack))
   109  			err = e.err
   110  		} else {
   111  			buf.WriteString(fmt.Sprintf("%s\n", err.Error()))
   112  			err = nil
   113  		}
   114  	}
   115  	return buf.String()
   116  }
   117  
   118  // The functions `Is`, `As` & `Unwrap` provides a thin wrapper around the builtin errors
   119  // package in go. Just for sake of completeness and correct autocompletion behaviors from
   120  // IDEs they have been wrapped using functions instead of using variable to reference them
   121  // as first class functions (eg: var Is = errros.Is ).
   122  
   123  // Is is a wrapper of built-in errors.Is. It reports whether any error in err's
   124  // chain matches target. The chain consists of err itself followed by the sequence
   125  // of errors obtained by repeatedly calling Unwrap.
   126  func Is(err, target error) bool {
   127  	return errors.Is(err, target)
   128  }
   129  
   130  // As is a wrapper of built-in errors.As. It finds the first error in err's
   131  // chain that matches target, and if one is found, sets target to that error
   132  // value and returns true. Otherwise, it returns false.
   133  func As(err error, target interface{}) bool {
   134  	return errors.As(err, target)
   135  }
   136  
   137  // Unwrap is a wrapper of built-in errors.Unwrap. Unwrap returns the result of
   138  // calling the Unwrap method on err, if err's type contains an Unwrap method
   139  // returning error. Otherwise, Unwrap returns nil.
   140  func Unwrap(err error) error {
   141  	return errors.Unwrap(err)
   142  }