gopkg.in/hedzr/errors.v3@v3.3.1/withstackinfo.go (about) 1 package errors 2 3 import ( 4 "fmt" 5 "io" 6 "reflect" 7 "strings" 8 ) 9 10 // WithStackInfo is exported now 11 type WithStackInfo struct { 12 causes2 13 14 *Stack 15 16 sites []interface{} //nolint:revive 17 taggedSites map[string]interface{} //nolint:revive 18 } 19 20 func (w *WithStackInfo) IsDescended(descendant error) bool { 21 if e, ok := descendant.(*WithStackInfo); ok { 22 return e.Code == w.Code && e.msg == w.msg 23 } 24 return false 25 } 26 27 // String for stringer interface 28 func (w *WithStackInfo) String() string { return w.Error() } 29 30 // WithStack annotates err with a Stack trace at the point WithStack 31 // was called. 32 // 33 // If err is nil, WithStack returns nil. 34 func WithStack(cause error) error { 35 return withStackZ(cause) 36 } 37 38 func withStackZ(cause error) *WithStackInfo { 39 if cause == nil { 40 return nil 41 } 42 return &WithStackInfo{causes2: causes2{Causers: []error{cause}}, Stack: callers(1)} 43 } 44 45 // End ends the WithXXX stream calls while you dislike unwanted `err =`. 46 // 47 // For instance, the construction of an error without warnings looks like: 48 // 49 // err := New("hello %v", "world") 50 // _ = err. 51 // WithErrors(io.EOF, io.ErrShortWrite). 52 // WithErrors(io.ErrClosedPipe). 53 // WithCode(Internal) 54 // 55 // To avoid the `_ =`, you might beloved with a End() call: 56 // 57 // err := New("hello %v", "world") 58 // err.WithErrors(io.EOF, io.ErrShortWrite). 59 // WithErrors(io.ErrClosedPipe). 60 // WithCode(Internal). 61 // End() 62 func (w *WithStackInfo) End() {} 63 64 // Data returns the wrapped common user data by WithData. 65 // The error objects with passed WithData will be moved into inner 66 // errors set, so its are excluded from Data(). 67 func (w *WithStackInfo) Data() []interface{} { return w.sites } //nolint:revive 68 69 // TaggedData returns the wrapped tagged user data by WithTaggedData. 70 func (w *WithStackInfo) TaggedData() TaggedData { return w.taggedSites } 71 72 // Cause returns the underlying cause of the error, if possible. 73 // An error value has a cause if it implements the following 74 // interface: 75 // 76 // type causer interface { 77 // Cause() error 78 // } 79 // 80 // If an error object does not implement Cause interface, the 81 // original error object will be returned. 82 // If the error is nil, nil will be returned without further 83 // investigation. 84 func (w *WithStackInfo) Cause() error { 85 return w.causes2.Cause() 86 } 87 88 func (w *WithStackInfo) rebuild() Buildable { 89 return w 90 } 91 92 // WithSkip specifies a special number of stack frames that will 93 // be ignored. 94 func (w *WithStackInfo) WithSkip(skip int) Buildable { 95 w.Stack = callers(skip) 96 return w 97 } 98 99 // WithMessage formats the error message 100 func (w *WithStackInfo) WithMessage(message string, args ...interface{}) Buildable { //nolint:revive 101 _ = w.causes2.WithMessage(message, args...) 102 return w 103 } 104 105 // WithCode specifies an error code. 106 // An error code `Code` is a integer number with error interface 107 // supported. 108 func (w *WithStackInfo) WithCode(code Code) Buildable { 109 w.Code = code 110 return w.rebuild() 111 } 112 113 // Attach collects the errors except an error is nil. 114 // 115 // StackTrace of errs will be copied to callee so that you can get a 116 // trace output nearer by the last error. 117 // 118 // Since v3.0.5, we break Attach() and remove its returning value. 119 // So WithStackInfo is a Container compliant type now. 120 func (w *WithStackInfo) Attach(errs ...error) { 121 // _ = w.WithErrors(errs...) 122 123 for _, e := range errs { 124 if e != nil { 125 w.Causers = append(w.Causers, e) 126 } 127 } 128 129 for _, e := range errs { 130 if e1, ok := e.(*WithStackInfo); ok { 131 w.Stack = e1.Stack 132 } 133 } 134 } 135 136 // WithErrors attaches the given errs as inner errors. 137 // 138 // WithErrors is similar with Attach() except it collects 139 // thees errors: 140 // 141 // 1. For an error implemented IsEmpty(), only if it is 142 // not empty (i.e. IsEmpty() return false). So the inner 143 // errors within an error container will be moved out 144 // from that container, and be copied into this holder. 145 // 146 // 2. For a normal error, such as io.EOF, just add it. 147 // 148 // It wraps the inner errors into underlying container and 149 // represents them all in a singular up-level error object. 150 // The wrapped inner errors can be retrieved with errors.Causes: 151 // 152 // var err = errors.New("hello").WithErrors(io.EOF, io.ShortBuffers) 153 // var errs []error = errors.Causes(err) 154 // 155 // Or, use As() to extract its: 156 // 157 // var errs []error 158 // errors.As(err, &errs) 159 func (w *WithStackInfo) WithErrors(errs ...error) Buildable { 160 _ = w.causes2.WithErrors(errs...) 161 162 // for _, e := range errs { 163 // if e1, ok := e.(*WithStackInfo); ok { 164 // w.Stack = e1.Stack 165 // } 166 // } 167 return w 168 } 169 170 // WithData appends errs if the general object is a error object. 171 // 172 // StackTrace of errs will be copied to callee so that you can get a 173 // trace output nearer by the last error. 174 // 175 // defer-recover block typically is a better place of WithData(). 176 // 177 // For example: 178 // 179 // defer func() { 180 // if e := recover(); e != nil { 181 // err = errors.New("[recovered] copyTo unsatisfied ([%v] %v -> [%v] %v), causes: %v", 182 // c.indirectType(from.Type()), from, c.indirectType(to.Type()), to, e). 183 // WithData(e) // StackTrace of e -> err 184 // n := log.CalcStackFrames(1) // skip defer-recover frame at first 185 // log.Skip(n).Errorf("%v", err) // skip go-lib frames and defer-recover frame, back to the point throwing panic 186 // } 187 // }() 188 func (w *WithStackInfo) WithData(errs ...interface{}) Buildable { //nolint:revive 189 if len(errs) > 0 { 190 for _, e := range errs { 191 if e1, ok := e.(error); ok { 192 _ = w.WithErrors(e1) 193 if e1, ok := e.(*WithStackInfo); ok { 194 w.Stack = e1.Stack 195 } 196 } else if e != nil { 197 w.sites = append(w.sites, e) 198 } 199 } 200 } 201 return w 202 } 203 204 // WithTaggedData appends errs if the general object is a error object 205 func (w *WithStackInfo) WithTaggedData(siteScenes TaggedData) Buildable { 206 if w.taggedSites == nil { 207 w.taggedSites = make(TaggedData) 208 } 209 for k, v := range siteScenes { 210 w.taggedSites[k] = v 211 } 212 return w 213 } 214 215 // WithCause sets the underlying error manually if necessary. 216 func (w *WithStackInfo) WithCause(cause error) Buildable { 217 w.causes2.Causers = append(w.causes2.Causers, cause) 218 return w 219 } 220 221 // WithMaxObjectStringLength set limitation for object stringify length. 222 // 223 // The objects of Data/TaggedData will be limited while its' been formatted with "%+v" 224 func (w *WithStackInfo) WithMaxObjectStringLength(maxlen int) Buildable { 225 w.causes2.WithMaxObjectStringLength(maxlen) //nolint:errcheck 226 return w 227 } 228 229 // Defer can be used as a defer function to simplify your codes. 230 // 231 // The codes: 232 // 233 // func some(){ 234 // // as a inner errors container 235 // child := func() (err error) { 236 // errContainer := errors.New("") 237 // defer errContainer.Defer(&err) 238 // 239 // for _, r := range []error{io.EOF, io.ErrClosedPipe, errors.Internal} { 240 // errContainer.Attach(r) 241 // } 242 // 243 // return 244 // } 245 // 246 // err := child() 247 // t.Logf("failed: %+v", err) 248 // } 249 func (w *WithStackInfo) Defer(err *error) { //nolint:gocritic 250 if w.IsEmpty() { 251 *err = nil 252 } else { 253 *err = w // no inner errors attached into an error container, that assumed 'is empty' 254 } 255 } 256 257 func (w *WithStackInfo) Clear() Container { 258 w.msg = "" 259 w.sites = nil 260 w.taggedSites = nil 261 w.Causers = nil 262 w.liveArgs = nil 263 return w 264 } 265 266 // IsEmpty tests has attached errors 267 func (w *WithStackInfo) IsEmpty() bool { 268 return len(w.sites) == 0 && len(w.taggedSites) == 0 && w.causes2.IsEmpty() 269 } 270 271 // FormatWith _ 272 func (w *WithStackInfo) FormatWith(args ...interface{}) error { //nolint:revive 273 c := w.Clone() 274 c.liveArgs = args 275 return c 276 } 277 278 // Clone _ 279 func (w *WithStackInfo) Clone() *WithStackInfo { 280 c := &WithStackInfo{ 281 causes2: causes2{ 282 Code: w.causes2.Code, 283 Causers: w.causes2.Causers, 284 msg: w.causes2.msg, 285 unwrapIndex: w.causes2.unwrapIndex, 286 liveArgs: w.causes2.liveArgs, 287 }, 288 Stack: w.Stack, 289 sites: w.sites, 290 taggedSites: w.taggedSites, 291 } 292 return c 293 } 294 295 func snfmt(sb *strings.Builder, format string, args ...interface{}) (n int) { //nolint:revive 296 str := fmt.Sprintf(format, args...) 297 // n = len(str) 298 n, _ = sb.WriteString(str) 299 return 300 } 301 302 // Format formats the stack of Frames according to the fmt.Formatter interface. 303 // 304 // %s lists source files for each Frame in the stack 305 // %v lists the source file and line number for each Frame in the stack 306 // 307 // Format accepts flags that alter the printing of some verbs, as follows: 308 // 309 // %+v Prints filename, function, and line number for each Frame in the stack. 310 func (w *WithStackInfo) Format(s fmt.State, verb rune) { //nolint:revive 311 switch verb { 312 case 'v': 313 if s.Flag('+') { 314 var sb strings.Builder 315 n := snfmt(&sb, "%+v", w.makeErrorString(true)) 316 if len(w.sites) > 0 { 317 if n > 0 { 318 n += snfmt(&sb, "\n ") 319 } 320 // n += snfmt(&sb, "Sites: %+v", w.sites) 321 n += snfmt(&sb, "Sites:\n") 322 for i, site := range w.sites { 323 n += snfmt(&sb, " %d. %+v\n", i+1, w.limitObj(site)) 324 } 325 } 326 if len(w.taggedSites) > 0 { 327 if n > 0 { 328 n += snfmt(&sb, "\n ") 329 } 330 // snfmt(&sb, "Tagged Sites: %+v", w.taggedSites) 331 n += snfmt(&sb, "Tagged Sites:\n") 332 for k, site := range w.taggedSites { 333 n += snfmt(&sb, " %v => %+v\n", k, w.limitObj(site)) 334 } 335 } 336 _, _ = fmt.Fprint(s, sb.String()) 337 w.Stack.Format(s, verb) 338 return 339 } 340 _, _ = fmt.Fprintf(s, "%v", w.Error()) 341 case 's': 342 _, _ = io.WriteString(s, w.Error()) 343 case 'q': 344 _, _ = fmt.Fprintf(s, "%q", w.Error()) 345 } 346 } 347 348 // Is reports whether any error in `err`'s chain matches target. 349 func (w *WithStackInfo) Is(target error) bool { 350 if te, ok := target.(*WithStackInfo); ok { 351 return w.equal(te) 352 } 353 for _, e := range w.Causers { 354 if Is(e, target) { 355 return true 356 } 357 } 358 return w.causes2.Is(target) 359 } 360 361 func (w *WithStackInfo) equal(target *WithStackInfo) bool { 362 if w.causes2.equal(&target.causes2) && 363 reflect.DeepEqual(w.sites, target.sites) && 364 reflect.DeepEqual(w.taggedSites, target.taggedSites) { 365 return true 366 } 367 368 for _, e := range w.Causers { 369 if Is(target, e) { 370 return true 371 } 372 } 373 374 return false 375 } 376 377 // // TypeIs reports whether any error in `err`'s chain matches target. 378 // func (w *WithStackInfo) TypeIs(target error) bool { 379 // if x, ok := w.error.(interface{ TypeIs(error) bool }); ok && x.TypeIs(target) { 380 // return true 381 // } 382 // return w.error == target 383 // } 384 385 // // As finds the first error in `err`'s chain that matches target, and if so, sets 386 // // target to that error value and returns true. 387 // func (w *WithStackInfo) As(target interface{}) bool { 388 // return As(w.error, target) 389 // //if target == nil { 390 // // panic("errors: target cannot be nil") 391 // //} 392 // //val := reflect.ValueOf(target) 393 // //typ := val.Type() 394 // //if typ.Kind() != reflect.Ptr || val.IsNil() { 395 // // panic("errors: target must be a non-nil pointer") 396 // //} 397 // //if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { 398 // // panic("errors: *target must be interface or implement error") 399 // //} 400 // //targetType := typ.Elem() 401 // //err := w.error 402 // //for err != nil { 403 // // if reflect.TypeOf(err).AssignableTo(targetType) { 404 // // val.Elem().Set(reflect.ValueOf(err)) 405 // // return true 406 // // } 407 // // if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { 408 // // return true 409 // // } 410 // // err = Unwrap(err) 411 // //} 412 // //return false 413 // } 414 415 // // Unwrap returns the result of calling the Unwrap method on err, if 416 // // `err`'s type contains an Unwrap method returning error. 417 // // Otherwise, Unwrap returns nil. 418 // func (w *WithStackInfo) Unwrap() error { 419 // if w.error != nil { 420 // return w.error 421 // } 422 // //if x, ok := w.error.(interface{ Unwrap() error }); ok { 423 // // return x.Unwrap() 424 // //} 425 // return nil 426 // } 427 428 // // IsEmpty tests has attached errors 429 // func (w *WithStackInfo) IsEmpty() bool { 430 // if x, ok := w.error.(interface{ IsEmpty() bool }); ok { 431 // return x.IsEmpty() 432 // } 433 // return false 434 // }