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 }