github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/errors/errors.go (about)

     1  package errors
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  )
     8  
     9  // New returns an error that formats as the given text.
    10  func New(text string) error {
    11  	return errors.New(text)
    12  }
    13  
    14  // wrapperError satisfies the error interface.
    15  type wrapperError struct {
    16  	msg    string
    17  	detail []string
    18  	data   map[string]interface{}
    19  	stack  []StackFrame
    20  	root   error
    21  }
    22  
    23  // It satisfies the error interface.
    24  func (e wrapperError) Error() string {
    25  	return e.msg
    26  }
    27  
    28  // Root returns the original error that was wrapped by one or more
    29  // calls to Wrap. If e does not wrap other errors, it will be returned
    30  // as-is.
    31  func Root(e error) error {
    32  	if wErr, ok := e.(wrapperError); ok {
    33  		return wErr.root
    34  	}
    35  	return e
    36  }
    37  
    38  // wrap adds a context message and stack trace to err and returns a new error
    39  // containing the new context. This function is meant to be composed within
    40  // other exported functions, such as Wrap and WithDetail.
    41  // The argument stackSkip is the number of stack frames to ascend when
    42  // generating stack straces, where 0 is the caller of wrap.
    43  func wrap(err error, msg string, stackSkip int) error {
    44  	if err == nil {
    45  		return nil
    46  	}
    47  
    48  	werr, ok := err.(wrapperError)
    49  	if !ok {
    50  		werr.root = err
    51  		werr.msg = err.Error()
    52  		werr.stack = getStack(stackSkip+2, stackTraceSize)
    53  	}
    54  	if msg != "" {
    55  		werr.msg = msg + ": " + werr.msg
    56  	}
    57  
    58  	return werr
    59  }
    60  
    61  // Wrap adds a context message and stack trace to err and returns a new error
    62  // with the new context. Arguments are handled as in fmt.Print.
    63  // Use Root to recover the original error wrapped by one or more calls to Wrap.
    64  // Use Stack to recover the stack trace.
    65  // Wrap returns nil if err is nil.
    66  func Wrap(err error, a ...interface{}) error {
    67  	if err == nil {
    68  		return nil
    69  	}
    70  	return wrap(err, fmt.Sprint(a...), 1)
    71  }
    72  
    73  // Wrapf is like Wrap, but arguments are handled as in fmt.Printf.
    74  func Wrapf(err error, format string, a ...interface{}) error {
    75  	if err == nil {
    76  		return nil
    77  	}
    78  	return wrap(err, fmt.Sprintf(format, a...), 1)
    79  }
    80  
    81  // WithDetail returns a new error that wraps
    82  // err as a chain error messsage containing text
    83  // as its additional context.
    84  // Function Detail will return the given text
    85  // when called on the new error value.
    86  func WithDetail(err error, text string) error {
    87  	if err == nil {
    88  		return nil
    89  	}
    90  	if text == "" {
    91  		return err
    92  	}
    93  	e1 := wrap(err, text, 1).(wrapperError)
    94  	e1.detail = append(e1.detail, text)
    95  	return e1
    96  }
    97  
    98  // WithDetailf is like WithDetail, except it formats
    99  // the detail message as in fmt.Printf.
   100  // Function Detail will return the formatted text
   101  // when called on the new error value.
   102  func WithDetailf(err error, format string, v ...interface{}) error {
   103  	if err == nil {
   104  		return nil
   105  	}
   106  	text := fmt.Sprintf(format, v...)
   107  	e1 := wrap(err, text, 1).(wrapperError)
   108  	e1.detail = append(e1.detail, text)
   109  	return e1
   110  }
   111  
   112  // Detail returns the detail message contained in err, if any.
   113  // An error has a detail message if it was made by WithDetail
   114  // or WithDetailf.
   115  func Detail(err error) string {
   116  	wrapper, ok := err.(wrapperError)
   117  	if !ok {
   118  		return err.Error()
   119  	}
   120  	return strings.Join(wrapper.detail, "; ")
   121  }
   122  
   123  // withData returns a new error that wraps err
   124  // as a chain error message containing v as
   125  // an extra data item.
   126  // Calling Data on the returned error yields v.
   127  // Note that if err already has a data item,
   128  // it will not be accessible via the returned error value.
   129  func withData(err error, v map[string]interface{}) error {
   130  	if err == nil {
   131  		return nil
   132  	}
   133  	e1 := wrap(err, "", 1).(wrapperError)
   134  	e1.data = v
   135  	return e1
   136  }
   137  
   138  // WithData returns a new error that wraps err
   139  // as a chain error message containing a value of type
   140  // map[string]interface{} as an extra data item.
   141  // The map contains the values in the map in err,
   142  // if any, plus the items in keyval.
   143  // Keyval takes the form
   144  //   k1, v1, k2, v2, ...
   145  // Values kN must be strings.
   146  // Calling Data on the returned error yields the map.
   147  // Note that if err already has a data item of any other type,
   148  // it will not be accessible via the returned error value.
   149  func WithData(err error, keyval ...interface{}) error {
   150  	if err == nil {
   151  		return nil
   152  	}
   153  	// TODO(kr): add vet check for odd-length keyval and non-string keys
   154  	newkv := make(map[string]interface{})
   155  	for k, v := range Data(err) {
   156  		newkv[k] = v
   157  	}
   158  	for i := 0; i < len(keyval); i += 2 {
   159  		newkv[keyval[i].(string)] = keyval[i+1]
   160  	}
   161  	return withData(err, newkv)
   162  }
   163  
   164  // Data returns the data item in err, if any.
   165  func Data(err error) map[string]interface{} {
   166  	wrapper, _ := err.(wrapperError)
   167  	return wrapper.data
   168  }
   169  
   170  // Sub returns an error containing root as its root and
   171  // taking all other metadata (stack trace, detail, message,
   172  // and data items) from err.
   173  //
   174  // Sub returns nil when either root or err is nil.
   175  //
   176  // Use this when you need to substitute a new root error in place
   177  // of an existing error that may already hold a stack trace
   178  // or other metadata.
   179  func Sub(root, err error) error {
   180  	if wrapper, ok := err.(wrapperError); ok && root != nil {
   181  		wrapper.root = Root(root)
   182  		wrapper.msg = root.Error()
   183  		root = wrapper
   184  	}
   185  	if err == nil {
   186  		return nil
   187  	}
   188  	return Wrap(root, err.Error())
   189  }