go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/errutil/multi.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package errutil 9 10 import ( 11 "errors" 12 "fmt" 13 "strings" 14 ) 15 16 // Multi represents an array of errors. 17 type Multi []error 18 19 // Error implements error. 20 func (m Multi) Error() string { 21 if len(m) == 0 { 22 return "" 23 } 24 if len(m) == 1 { 25 return m[0].Error() 26 } 27 points := make([]string, len(m)) 28 for i, err := range m { 29 points[i] = fmt.Sprintf("* %v", err) 30 } 31 return fmt.Sprintf( 32 "%d errors occurred:\n\t%s\n\n", 33 len(m), strings.Join(points, "\n\t")) 34 } 35 36 // WrappedErrors implements something in errors. 37 func (m Multi) WrappedErrors() []error { 38 return m 39 } 40 41 // Unwrap returns an error from Error (or nil if there are no errors). 42 // This error returned will further support Unwrap to get the next error, 43 // etc. 44 // 45 // The resulting error supports errors.As/Is/Unwrap so you can continue 46 // to use the stdlib errors package to introspect further. 47 // 48 // This will perform a shallow copy of the errors slice. Any errors appended 49 // to this error after calling Unwrap will not be available until a new 50 // Unwrap is called on the multierror.Error. 51 func (m Multi) Unwrap() error { 52 if len(m) == 0 { 53 return nil 54 } 55 if len(m) == 1 { 56 return m[0] 57 } 58 errs := make([]error, len(m)) 59 copy(errs, m) 60 return chain(errs) 61 } 62 63 // chain implements the interfaces necessary for errors.Is/As/Unwrap to 64 // work in a deterministic way with multierror. A chain tracks a list of 65 // errors while accounting for the current represented error. This lets 66 // Is/As be meaningful. 67 // 68 // Unwrap returns the next error. In the cleanest form, Unwrap would return 69 // the wrapped error here but we can't do that if we want to properly 70 // get access to all the errors. Instead, users are recommended to use 71 // Is/As to get the correct error type out. 72 // 73 // Precondition: []error is non-empty (len > 0) 74 type chain []error 75 76 // Error implements the error interface 77 func (e chain) Error() string { 78 return e[0].Error() 79 } 80 81 // Unwrap implements errors.Unwrap by returning the next error in the 82 // chain or nil if there are no more errors. 83 func (e chain) Unwrap() error { 84 if len(e) == 1 { 85 return nil 86 } 87 88 return e[1:] 89 } 90 91 // As implements errors.As by attempting to map to the current value. 92 func (e chain) As(target interface{}) bool { 93 return errors.As(e[0], target) 94 } 95 96 // Is implements errors.Is by comparing the current value directly. 97 func (e chain) Is(target error) bool { 98 return errors.Is(e[0], target) 99 }