github.com/haraldrudell/parl@v0.4.176/perrors/errorglue/error-list.go (about)

     1  /*
     2  © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package errorglue
     7  
     8  import (
     9  	"errors"
    10  
    11  	"golang.org/x/exp/slices"
    12  )
    13  
    14  // ErrorList returns the list of associated errors enclosed in all error chains of err
    15  //   - the returned slice is a list of error chains beginning with the initial err
    16  //   - If err is nil, a nil slice is returned
    17  //   - duplicate error values are ignored
    18  //   - order is:
    19  //   - — associated errors from err’s error chain, oldest first
    20  //   - — then associated errors from other error chains, oldest found error of each chain first,
    21  //     and errors from the last found error chain first
    22  //   - —
    23  //   - associated errors are independent error chains that allows a single error
    24  //     to enclose addditional errors not part of its error chain
    25  //   - cyclic error values are filtered out so that no error instance is returned more than once
    26  func ErrorList(err error) (errs []error) {
    27  	if err == nil {
    28  		return // nil error return: nil slice
    29  	}
    30  	// errMap ensures that no error instance occurs more than once
    31  	var errMap = map[error]bool{err: true}
    32  
    33  	// process all error chains referred by err and its associated errors
    34  	//	- begin with err
    35  	for errorChains := []error{err}; len(errorChains) > 0; errorChains = errorChains[1:] {
    36  		// traverse error chain
    37  		for errorChain := errorChains[0]; errorChain != nil; errorChain = errors.Unwrap(errorChain) {
    38  
    39  			// find any associated error not found before
    40  			if relatedError, ok := errorChain.(RelatedError); ok {
    41  				if associatedError := relatedError.AssociatedError(); associatedError != nil {
    42  					if _, ok := errMap[associatedError]; !ok {
    43  
    44  						// store associated error, newest first
    45  						errs = append(errs, associatedError)
    46  						// store the error chain for scanning, first found first
    47  						errorChains = append(errorChains, associatedError)
    48  						// store associateed error to ensure uniqueness
    49  						errMap[associatedError] = true
    50  					}
    51  				}
    52  			}
    53  		}
    54  	}
    55  
    56  	// errs begins with err, then associated errors, oldest first
    57  	errs = append(errs, err)
    58  	slices.Reverse(errs)
    59  
    60  	return
    61  }