github.com/m3db/m3@v1.5.0/src/x/errors/errors.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 // Package errors provides utilities for working with different types errors. 22 package errors 23 24 import ( 25 "bytes" 26 "errors" 27 "fmt" 28 ) 29 30 // FirstError returns the first non nil error. 31 func FirstError(errs ...error) error { 32 for i := range errs { 33 if errs[i] != nil { 34 return errs[i] 35 } 36 } 37 return nil 38 } 39 40 type containedError struct { 41 inner error 42 } 43 44 func (e containedError) Error() string { 45 return e.inner.Error() 46 } 47 48 func (e containedError) InnerError() error { 49 return e.inner 50 } 51 52 // ContainedError is an error with a contained error. 53 type ContainedError interface { 54 InnerError() error 55 } 56 57 // InnerError returns the packaged inner error if this is an error that 58 // contains another. 59 func InnerError(err error) error { 60 contained, ok := err.(ContainedError) 61 if !ok { 62 return nil 63 } 64 return contained.InnerError() 65 } 66 67 type renamedError struct { 68 containedError 69 renamed error 70 } 71 72 // NewRenamedError returns a new error that packages an inner error with 73 // a renamed error. 74 func NewRenamedError(inner, renamed error) error { 75 return renamedError{containedError{inner}, renamed} 76 } 77 78 func (e renamedError) Error() string { 79 return e.renamed.Error() 80 } 81 82 func (e renamedError) InnerError() error { 83 return e.inner 84 } 85 86 type invalidParamsError struct { 87 containedError 88 } 89 90 // Wrap wraps an error with a message but preserves the type of the error. 91 func Wrap(err error, msg string) error { 92 renamed := errors.New(msg + ": " + err.Error()) 93 return NewRenamedError(err, renamed) 94 } 95 96 // Wrapf formats according to a format specifier and uses that string to 97 // wrap an error while still preserving the type of the error. 98 func Wrapf(err error, format string, args ...interface{}) error { 99 msg := fmt.Sprintf(format, args...) 100 return Wrap(err, msg) 101 } 102 103 // NewInvalidParamsError creates a new invalid params error 104 func NewInvalidParamsError(inner error) error { 105 return invalidParamsError{containedError{inner}} 106 } 107 108 func (e invalidParamsError) Error() string { 109 return e.inner.Error() 110 } 111 112 func (e invalidParamsError) InnerError() error { 113 return e.inner 114 } 115 116 // IsInvalidParams returns true if this is an invalid params error. 117 func IsInvalidParams(err error) bool { 118 return GetInnerInvalidParamsError(err) != nil 119 } 120 121 // GetInnerInvalidParamsError returns an inner invalid params error 122 // if contained by this error, nil otherwise. 123 func GetInnerInvalidParamsError(err error) error { 124 for err != nil { 125 if _, ok := err.(invalidParamsError); ok { 126 return InnerError(err) 127 } 128 // nolint:errorlint 129 if multiErr, ok := err.(MultiError); ok { 130 for _, e := range multiErr.Errors() { 131 if inner := GetInnerInvalidParamsError(e); err != nil { 132 return inner 133 } 134 } 135 } 136 err = InnerError(err) 137 } 138 return nil 139 } 140 141 type resourceExhaustedError struct { 142 containedError 143 } 144 145 // NewResourceExhaustedError creates a new resource exhausted error 146 func NewResourceExhaustedError(inner error) error { 147 return resourceExhaustedError{containedError{inner}} 148 } 149 150 func (e resourceExhaustedError) Error() string { 151 return e.inner.Error() 152 } 153 154 func (e resourceExhaustedError) InnerError() error { 155 return e.inner 156 } 157 158 // IsResourceExhausted returns true if this is a resource exhausted error. 159 func IsResourceExhausted(err error) bool { 160 return GetInnerResourceExhaustedError(err) != nil 161 } 162 163 // GetInnerResourceExhaustedError returns an inner resource exhausted error 164 // if contained by this error, nil otherwise. 165 func GetInnerResourceExhaustedError(err error) error { 166 for err != nil { 167 // nolint:errorlint 168 if _, ok := err.(resourceExhaustedError); ok { 169 return InnerError(err) 170 } 171 // nolint:errorlint 172 if multiErr, ok := err.(MultiError); ok { 173 for _, e := range multiErr.Errors() { 174 if inner := GetInnerResourceExhaustedError(e); err != nil { 175 return inner 176 } 177 } 178 } 179 err = InnerError(err) 180 } 181 return nil 182 } 183 184 // Is checks if the error is or contains the corresponding target error. 185 // It's intended to mimic the errors.Is functionality, but also consider xerrors' MultiError / InnerError 186 // wrapping functionality. 187 func Is(err, target error) bool { 188 for err != nil { 189 if errors.Is(err, target) { 190 return true 191 } 192 193 // nolint:errorlint 194 if multiErr, ok := err.(MultiError); ok { 195 for _, e := range multiErr.Errors() { 196 if Is(e, target) { 197 return true 198 } 199 } 200 } 201 202 err = InnerError(err) 203 } 204 return false 205 } 206 207 type retryableError struct { 208 containedError 209 } 210 211 // NewRetryableError creates a new retryable error. 212 func NewRetryableError(inner error) error { 213 return retryableError{containedError{inner}} 214 } 215 216 func (e retryableError) Error() string { 217 return e.inner.Error() 218 } 219 220 func (e retryableError) InnerError() error { 221 return e.inner 222 } 223 224 // IsRetryableError returns true if this is a retryable error. 225 func IsRetryableError(err error) bool { 226 return GetInnerRetryableError(err) != nil 227 } 228 229 // GetInnerRetryableError returns an inner retryable error 230 // if contained by this error, nil otherwise. 231 func GetInnerRetryableError(err error) error { 232 for err != nil { 233 if _, ok := err.(retryableError); ok { 234 return InnerError(err) 235 } 236 // nolint:errorlint 237 if multiErr, ok := err.(MultiError); ok { 238 for _, e := range multiErr.Errors() { 239 if inner := GetInnerRetryableError(e); err != nil { 240 return inner 241 } 242 } 243 } 244 err = InnerError(err) 245 } 246 return nil 247 } 248 249 type nonRetryableError struct { 250 containedError 251 } 252 253 // NewNonRetryableError creates a new non-retryable error. 254 func NewNonRetryableError(inner error) error { 255 return nonRetryableError{containedError{inner}} 256 } 257 258 func (e nonRetryableError) Error() string { 259 return e.inner.Error() 260 } 261 262 func (e nonRetryableError) InnerError() error { 263 return e.inner 264 } 265 266 // IsNonRetryableError returns true if this is a non-retryable error. 267 func IsNonRetryableError(err error) bool { 268 return GetInnerNonRetryableError(err) != nil 269 } 270 271 // GetInnerNonRetryableError returns an inner non-retryable error 272 // if contained by this error, nil otherwise. 273 func GetInnerNonRetryableError(err error) error { 274 for err != nil { 275 if _, ok := err.(nonRetryableError); ok { 276 return InnerError(err) 277 } 278 // nolint:errorlint 279 if multiErr, ok := err.(MultiError); ok { 280 for _, e := range multiErr.Errors() { 281 if inner := GetInnerNonRetryableError(e); err != nil { 282 return inner 283 } 284 } 285 } 286 err = InnerError(err) 287 } 288 return nil 289 } 290 291 // IsMultiError returns true if this is a multi-error error. 292 func IsMultiError(err error) bool { 293 _, ok := GetInnerMultiError(err) 294 return ok 295 } 296 297 // GetInnerMultiError returns an inner multi-error error 298 // if contained by this error, nil otherwise. 299 func GetInnerMultiError(err error) (MultiError, bool) { 300 for err != nil { 301 if v, ok := err.(MultiError); ok { 302 return v, true 303 } 304 err = InnerError(err) 305 } 306 return MultiError{}, false 307 } 308 309 // MultiError is an immutable error that packages a list of errors. 310 // 311 // TODO(xichen): we may want to limit the number of errors included. 312 type MultiError struct { 313 err error // optimization for single error case 314 errors []error 315 } 316 317 // NewMultiError creates a new MultiError object. 318 func NewMultiError() MultiError { 319 return MultiError{} 320 } 321 322 // Empty returns true if the MultiError has no errors. 323 func (e MultiError) Empty() bool { 324 return e.err == nil 325 } 326 327 func (e MultiError) Error() string { 328 if e.err == nil { 329 return "" 330 } 331 if len(e.errors) == 0 { 332 return e.err.Error() 333 } 334 var b bytes.Buffer 335 for i := range e.errors { 336 b.WriteString(e.errors[i].Error()) 337 b.WriteString("\n") 338 } 339 b.WriteString(e.err.Error()) 340 return b.String() 341 } 342 343 // Errors returns all the errors to inspect individually. 344 func (e MultiError) Errors() []error { 345 if e.err == nil { 346 return nil // No errors 347 } 348 // Need to prepend the first error to result 349 // since we avoid allocating array if we don't need it 350 // when we accumulate the first error 351 result := make([]error, 1+len(e.errors)) 352 result[0] = e.err 353 copy(result[1:], e.errors) 354 return result 355 } 356 357 // Contains returns true if any of the errors match the provided error using the Is check. 358 func (e MultiError) Contains(err error) bool { 359 if errors.Is(e.err, err) { 360 return true 361 } 362 for _, e := range e.errors { 363 if errors.Is(e, err) { 364 return true 365 } 366 } 367 return false 368 } 369 370 // Add adds an error returns a new MultiError object. 371 func (e MultiError) Add(err error) MultiError { 372 if err == nil { 373 return e 374 } 375 me := e 376 if me.err == nil { 377 me.err = err 378 return me 379 } 380 me.errors = append(me.errors, me.err) 381 me.err = err 382 return me 383 } 384 385 // FinalError returns all concatenated error messages if any. 386 func (e MultiError) FinalError() error { 387 if e.err == nil { 388 return nil 389 } 390 return e 391 } 392 393 // LastError returns the last received error if any. 394 func (e MultiError) LastError() error { 395 if e.err == nil { 396 return nil 397 } 398 return e.err 399 } 400 401 // NumErrors returns the total number of errors. 402 func (e MultiError) NumErrors() int { 403 if e.err == nil { 404 return 0 405 } 406 return len(e.errors) + 1 407 } 408 409 // Errors is a slice of errors that itself is an error too. 410 type Errors []error 411 412 // Error implements error. 413 func (e Errors) Error() string { 414 buf := bytes.NewBuffer(nil) 415 buf.WriteString("[") 416 for i, err := range e { 417 if err == nil { 418 buf.WriteString("<nil>") 419 } else { 420 buf.WriteString("<") 421 buf.WriteString(err.Error()) 422 buf.WriteString(">") 423 } 424 if i < len(e)-1 { 425 buf.WriteString(", ") 426 } 427 } 428 buf.WriteString("]") 429 return buf.String() 430 }