...

Source file src/github.com/goph/emperror/context.go

Documentation: github.com/goph/emperror

     1  package emperror
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  )
     7  
     8  // The implementation bellow is heavily influenced by go-kit's log context.
     9  
    10  // With returns a new error with keyvals context appended to it.
    11  // If the wrapped error is already a contextual error created by With
    12  // keyvals is appended to the existing context, but a new error is returned.
    13  func With(err error, keyvals ...interface{}) error {
    14  	if err == nil {
    15  		return nil
    16  	}
    17  
    18  	if len(keyvals) == 0 {
    19  		return err
    20  	}
    21  
    22  	var kvs []interface{}
    23  
    24  	// extract context from previous error
    25  	if c, ok := err.(*withContext); ok {
    26  		err = c.err
    27  
    28  		kvs = append(kvs, c.keyvals...)
    29  
    30  		if len(kvs)%2 != 0 {
    31  			kvs = append(kvs, nil)
    32  		}
    33  	}
    34  
    35  	kvs = append(kvs, keyvals...)
    36  
    37  	if len(kvs)%2 != 0 {
    38  		kvs = append(kvs, nil)
    39  	}
    40  
    41  	// Limiting the capacity of the stored keyvals ensures that a new
    42  	// backing array is created if the slice must grow in With.
    43  	// Using the extra capacity without copying risks a data race.
    44  	return &withContext{
    45  		err:     err,
    46  		keyvals: kvs[:len(kvs):len(kvs)],
    47  	}
    48  }
    49  
    50  // Context extracts the context key-value pairs from an error (or error chain).
    51  func Context(err error) []interface{} {
    52  	type contextor interface {
    53  		Context() []interface{}
    54  	}
    55  
    56  	var kvs []interface{}
    57  
    58  	ForEachCause(err, func(err error) bool {
    59  		if cerr, ok := err.(contextor); ok {
    60  			kvs = append(cerr.Context(), kvs...)
    61  		}
    62  
    63  		return true
    64  	})
    65  
    66  	return kvs
    67  }
    68  
    69  // withContext annotates an error with context.
    70  type withContext struct {
    71  	err     error
    72  	keyvals []interface{}
    73  }
    74  
    75  func (w *withContext) Error() string {
    76  	return w.err.Error()
    77  }
    78  
    79  // Context returns the appended keyvals.
    80  func (w *withContext) Context() []interface{} {
    81  	return w.keyvals
    82  }
    83  
    84  func (w *withContext) Cause() error {
    85  	return w.err
    86  }
    87  
    88  func (w *withContext) Format(s fmt.State, verb rune) {
    89  	switch verb {
    90  	case 'v':
    91  		if s.Flag('+') {
    92  			_, _ = fmt.Fprintf(s, "%+v", w.Cause())
    93  			return
    94  		}
    95  
    96  		fallthrough
    97  
    98  	case 's':
    99  		_, _ = io.WriteString(s, w.Error())
   100  
   101  	case 'q':
   102  		_, _ = fmt.Fprintf(s, "%q", w.Error())
   103  	}
   104  }
   105  

View as plain text