emperror.dev/errors@v0.8.1/error_details.go (about) 1 package errors 2 3 import ( 4 "fmt" 5 ) 6 7 // WithDetails annotates err with with arbitrary key-value pairs. 8 func WithDetails(err error, details ...interface{}) error { 9 if err == nil { 10 return nil 11 } 12 13 if len(details) == 0 { 14 return err 15 } 16 17 if len(details)%2 != 0 { 18 details = append(details, nil) 19 } 20 21 var w *withDetails 22 if !As(err, &w) { 23 w = &withDetails{ 24 error: err, 25 } 26 27 err = w 28 } 29 30 // Limiting the capacity of the stored keyvals ensures that a new 31 // backing array is created if the slice must grow in With. 32 // Using the extra capacity without copying risks a data race. 33 d := append(w.details, details...) 34 w.details = d[:len(d):len(d)] 35 36 return err 37 } 38 39 // GetDetails extracts the key-value pairs from err's chain. 40 func GetDetails(err error) []interface{} { 41 var details []interface{} 42 43 // Usually there is only one error with details (when using the WithDetails API), 44 // but errors themselves can also implement the details interface exposing their attributes. 45 UnwrapEach(err, func(err error) bool { 46 if derr, ok := err.(interface{ Details() []interface{} }); ok { 47 details = append(derr.Details(), details...) 48 } 49 50 return true 51 }) 52 53 return details 54 } 55 56 // withDetails annotates an error with arbitrary key-value pairs. 57 type withDetails struct { 58 error error 59 details []interface{} 60 } 61 62 func (w *withDetails) Error() string { return w.error.Error() } 63 func (w *withDetails) Cause() error { return w.error } 64 func (w *withDetails) Unwrap() error { return w.error } 65 66 // Details returns the appended details. 67 func (w *withDetails) Details() []interface{} { 68 return w.details 69 } 70 71 func (w *withDetails) Format(s fmt.State, verb rune) { 72 switch verb { 73 case 'v': 74 if s.Flag('+') { 75 _, _ = fmt.Fprintf(s, "%+v", w.error) 76 77 return 78 } 79 80 _, _ = fmt.Fprintf(s, "%v", w.error) 81 82 case 's': 83 _, _ = fmt.Fprintf(s, "%s", w.error) 84 85 case 'q': 86 _, _ = fmt.Fprintf(s, "%q", w.error) 87 } 88 }