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  }