gopkg.in/hedzr/errors.v3@v3.3.1/causes.go (about) 1 package errors 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "strings" 9 ) 10 11 type causes2 struct { 12 Code 13 14 Causers []error 15 msg string 16 17 unwrapIndex int // simple index for iterating Unwrap 18 maxStringLen int // the output string max-length for an object (see also sites/taggedSites), negative or zero means no limit. 19 20 liveArgs []interface{} //nolint:revive // error message template ? 21 } 22 23 func (w *causes2) limitObj(obj interface{}) (s string) { //nolint:revive 24 s = fmt.Sprintf("%+v", obj) 25 if w.maxStringLen > 0 && len(s) > w.maxStringLen { 26 s = s[0:w.maxStringLen-3] + "..." 27 } 28 return 29 } 30 31 // WithMaxObjectStringLength for error interface 32 func (w *causes2) WithMaxObjectStringLength(maxlen int) *causes2 { 33 w.maxStringLen = maxlen 34 return w 35 } 36 37 // WithCode for error interface 38 func (w *causes2) WithCode(code Code) *causes2 { 39 w.Code = code 40 return w 41 } 42 43 func (w *causes2) WithMessage(message string, args ...interface{}) *causes2 { //nolint:revive 44 if len(args) > 0 { 45 message = fmt.Sprintf(message, args...) //nolint:revive 46 } 47 w.msg = message 48 return w 49 } 50 51 // End ends the WithXXX stream calls while you dislike unwanted `err =`. 52 func (w *causes2) End() {} 53 54 // Defer can be used as a defer function to simplify your codes. 55 // 56 // The codes: 57 // 58 // func some(){ 59 // // as a inner errors container 60 // child := func() (err error) { 61 // errContainer := errors.New("") 62 // defer errContainer.Defer(&err) 63 // 64 // for _, r := range []error{io.EOF, io.ErrClosedPipe, errors.Internal} { 65 // errContainer.Attach(r) 66 // } 67 // 68 // return 69 // } 70 // 71 // err := child() 72 // t.Logf("failed: %+v", err) 73 // } 74 func (w *causes2) Defer(err *error) { //nolint:gocritic 75 if w.IsEmpty() { 76 *err = nil 77 } else { 78 *err = w 79 } 80 } 81 82 func (w *causes2) Clear() Container { 83 w.msg = "" 84 w.Causers = nil 85 w.liveArgs = nil 86 w.Code = OK 87 w.unwrapIndex = 0 88 w.maxStringLen = 0 89 return w 90 } 91 92 // WithErrors appends errs 93 // 94 // WithStackInfo.Attach() can only wrap and hold one child error object. 95 // 96 // WithErrors attach child errors into an error container. 97 // For a container which has IsEmpty() interface, it would not 98 // be attached if it is empty (i.e. no errors). 99 // 100 // For a nil error object, it will be ignored. 101 func (w *causes2) WithErrors(errs ...error) *causes2 { //nolint:revive 102 for _, e := range errs { 103 if e != nil { 104 if check, ok := e.(interface{ IsEmpty() bool }); ok { 105 if !check.IsEmpty() { 106 w.Causers = append(w.Causers, e) 107 } else if e.Error() != "" { 108 w.Causers = append(w.Causers, e) 109 } 110 } else { 111 w.Causers = append(w.Causers, e) 112 } 113 } 114 } 115 return w 116 } 117 118 // Attach collects the errors except it's nil 119 func (w *causes2) Attach(errs ...error) { 120 // _ = w.WithErrors(errs...) 121 122 for _, e := range errs { 123 if e != nil { 124 w.Causers = append(w.Causers, e) 125 } 126 } 127 } 128 129 // FormatWith _ 130 func (w *causes2) FormatWith(args ...interface{}) error { //nolint:revive 131 c := w.Clone() 132 c.liveArgs = args 133 return c 134 } 135 136 // Clone _ 137 func (w *causes2) Clone() *causes2 { 138 c := &causes2{ 139 Code: w.Code, 140 Causers: w.Causers, 141 msg: w.msg, 142 unwrapIndex: w.unwrapIndex, 143 liveArgs: w.liveArgs, 144 } 145 return c 146 } 147 148 func (w *causes2) Error() string { 149 return w.makeErrorString(false) 150 } 151 152 func (w *causes2) makeErrorString(line bool) string { //nolint:revive 153 var buf bytes.Buffer 154 if w.msg != "" { 155 if len(w.liveArgs) > 0 { 156 msg := fmt.Sprintf(w.msg, w.liveArgs...) 157 _, _ = buf.WriteString(msg) 158 } else { 159 _, _ = buf.WriteString(w.msg) 160 } 161 } 162 163 if line { 164 _, _ = buf.WriteRune('\n') 165 if w.Code != OK { 166 _, _ = buf.WriteString(w.Code.String()) 167 _, _ = buf.WriteRune('\n') 168 } 169 170 for _, c := range w.Causers { 171 _, _ = buf.WriteString(" - ") 172 var xc *causes2 173 if As(c, &xc) { 174 _, _ = buf.WriteString(leftPad(xc.makeErrorString(line), " ", false)) 175 } else { 176 _, _ = buf.WriteString(leftPad(c.Error(), " ", false)) 177 } 178 } 179 180 // buf.WriteRune('\n') 181 return buf.String() 182 } 183 184 var needclose, needsep bool 185 if w.Code != OK { 186 if buf.Len() > 0 { 187 _, _ = buf.WriteRune(' ') 188 } 189 _, _ = buf.WriteString("[") 190 _, _ = buf.WriteString(w.Code.String()) 191 needclose = true 192 needsep = true 193 } 194 if w.msg == "" { 195 needsep = false 196 } 197 if len(w.Causers) > 0 { 198 if buf.Len() > 0 { 199 _, _ = buf.WriteRune(' ') 200 } 201 _, _ = buf.WriteString("[") 202 needclose = true 203 } 204 205 for i, c := range w.Causers { 206 if i > 0 || needsep { 207 _, _ = buf.WriteString(" | ") 208 } 209 _, _ = buf.WriteString(c.Error()) 210 } 211 if needclose { 212 _, _ = buf.WriteString("]") 213 } 214 // buf.WriteString(w.Stack) 215 return buf.String() 216 } 217 218 func leftPad(s, padStr string, firstLine bool) string { //nolint:revive 219 if padStr == "" { 220 return s 221 } 222 223 var ln int 224 var sb strings.Builder 225 scanner := bufio.NewScanner(bufio.NewReader(strings.NewReader(s))) 226 for scanner.Scan() { 227 if ln != 0 || firstLine { 228 _, _ = sb.WriteString(padStr) 229 } 230 _, _ = sb.WriteString(scanner.Text()) 231 _, _ = sb.WriteRune('\n') 232 ln++ 233 } 234 return sb.String() 235 } 236 237 // Format formats the stack of Frames according to the fmt.Formatter interface. 238 // 239 // %s lists source files for each Frame in the stack 240 // %v lists the source file and line number for each Frame in the stack 241 // 242 // Format accepts flags that alter the printing of some verbs, as follows: 243 // 244 // %+v Prints filename, function, and line number for each Frame in the stack. 245 func (w *causes2) Format(s fmt.State, verb rune) { 246 switch verb { 247 case 'v': 248 if s.Flag('+') { 249 _, _ = fmt.Fprintf(s, "%+v", w.makeErrorString(true)) 250 return 251 } 252 fallthrough 253 case 's': 254 _, _ = io.WriteString(s, w.Error()) 255 case 'q': 256 _, _ = fmt.Fprintf(s, "%q", w.Error()) 257 } 258 } 259 260 // String for stringer interface 261 func (w *causes2) String() string { return w.Error() } 262 263 func (w *causes2) Cause() error { 264 if len(w.Causers) == 0 { 265 return nil 266 } 267 return w.Causers[0] 268 } 269 270 // Causes simply returns the wrapped inner errors. 271 // It doesn't consider an wrapped Code entity is an inner error too. 272 // So if you wanna to extract any inner error objects, use 273 // errors.Unwrap for instead. The errors.Unwrap could extract all 274 // of them one by one: 275 // 276 // var err = errors.New("hello").WithErrors(io.EOF, io.ShortBuffers) 277 // var e error = err 278 // for e != nil { 279 // e = errors.Unwrap(err) 280 // } 281 func (w *causes2) Causes() []error { 282 if len(w.Causers) == 0 { 283 return nil 284 } 285 return w.Causers 286 } 287 288 func (w *causes2) Unwrap() error { 289 defer func() { w.unwrapIndex++ }() 290 291 if w.unwrapIndex >= 0 && w.unwrapIndex < len(w.Causers) { 292 return w.Causers[w.unwrapIndex] 293 } 294 if w.unwrapIndex == len(w.Causers) { 295 if w.Code != OK { 296 return w.Code 297 } 298 } 299 300 // reset index 301 w.unwrapIndex = -1 302 return nil 303 } 304 305 func (w *causes2) Reset() { 306 w.unwrapIndex = 0 307 } 308 309 // IsDescended test if ancestor is an error template and descendant 310 // is derived from it by calling ancestor.FormatWith. 311 func IsDescended(ancestor, descendant error) bool { 312 if a, ok := ancestor.(interface{ IsDescended(descendant error) bool }); ok { 313 return a.IsDescended(descendant) 314 } 315 return false 316 } 317 318 func (w *causes2) IsDescended(descendant error) bool { 319 if e, ok := descendant.(*causes2); ok { 320 return e.Code == w.Code && e.msg == w.msg 321 } 322 return false 323 } 324 325 func (w *causes2) Is(target error) bool { 326 if w.Code != OK { 327 if c, ok := target.(Code); ok && c == w.Code { 328 return true 329 } 330 } 331 if IsSlice(w.Causers, target) { 332 return true 333 } 334 if te, ok := target.(*causes2); ok { 335 return w.equal(te) 336 } 337 return false 338 } 339 340 func (w *causes2) equal(target *causes2) bool { 341 return w.Code == target.Code && 342 w.msg == target.msg && 343 w.arrayEqual(w.Causers, target.Causers) 344 } 345 346 func (w *causes2) arrayEqual(a, b []error) bool { 347 if len(a) != len(b) { 348 return false 349 } 350 yes := true 351 for i := 0; i < len(a); i++ { 352 if Is(a[i], b[i]) { 353 yes = false 354 break 355 } 356 } 357 return yes 358 } 359 360 func (w *causes2) TypeIs(target error) bool { 361 return TypeIsSlice(w.Causers, target) 362 } 363 364 // As finds the first error in `err`'s chain that matches target, 365 // and if so, sets target to that error value and returns true. 366 func (w *causes2) As(target interface{}) bool { //nolint:revive 367 if c, ok := target.(*Code); ok { 368 *c = w.Code 369 return true 370 } 371 if c, ok := target.(*[]error); ok { 372 *c = w.Causers 373 return true 374 } 375 if c, ok := target.(**causes2); ok { 376 *c = w 377 return true 378 } 379 return AsSlice(w.Causers, target) 380 } 381 382 // IsEmpty tests has attached errors 383 func (w *causes2) IsEmpty() bool { 384 return w.Code == OK && len(w.Causers) == 0 && len(w.liveArgs) == 0 385 }