...

Source file src/github.com/hashicorp/errwrap/errwrap.go

Documentation: github.com/hashicorp/errwrap

     1  // Package errwrap implements methods to formalize error wrapping in Go.
     2  //
     3  // All of the top-level functions that take an `error` are built to be able
     4  // to take any error, not just wrapped errors. This allows you to use errwrap
     5  // without having to type-check and type-cast everywhere.
     6  package errwrap
     7  
     8  import (
     9  	"errors"
    10  	"reflect"
    11  	"strings"
    12  )
    13  
    14  // WalkFunc is the callback called for Walk.
    15  type WalkFunc func(error)
    16  
    17  // Wrapper is an interface that can be implemented by custom types to
    18  // have all the Contains, Get, etc. functions in errwrap work.
    19  //
    20  // When Walk reaches a Wrapper, it will call the callback for every
    21  // wrapped error in addition to the wrapper itself. Since all the top-level
    22  // functions in errwrap use Walk, this means that all those functions work
    23  // with your custom type.
    24  type Wrapper interface {
    25  	WrappedErrors() []error
    26  }
    27  
    28  // Wrap defines that outer wraps inner, returning an error type that
    29  // can be cleanly used with the other methods in this package, such as
    30  // Contains, GetAll, etc.
    31  //
    32  // This function won't modify the error message at all (the outer message
    33  // will be used).
    34  func Wrap(outer, inner error) error {
    35  	return &wrappedError{
    36  		Outer: outer,
    37  		Inner: inner,
    38  	}
    39  }
    40  
    41  // Wrapf wraps an error with a formatting message. This is similar to using
    42  // `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap
    43  // errors, you should replace it with this.
    44  //
    45  // format is the format of the error message. The string '{{err}}' will
    46  // be replaced with the original error message.
    47  //
    48  // Deprecated: Use fmt.Errorf()
    49  func Wrapf(format string, err error) error {
    50  	outerMsg := "<nil>"
    51  	if err != nil {
    52  		outerMsg = err.Error()
    53  	}
    54  
    55  	outer := errors.New(strings.Replace(
    56  		format, "{{err}}", outerMsg, -1))
    57  
    58  	return Wrap(outer, err)
    59  }
    60  
    61  // Contains checks if the given error contains an error with the
    62  // message msg. If err is not a wrapped error, this will always return
    63  // false unless the error itself happens to match this msg.
    64  func Contains(err error, msg string) bool {
    65  	return len(GetAll(err, msg)) > 0
    66  }
    67  
    68  // ContainsType checks if the given error contains an error with
    69  // the same concrete type as v. If err is not a wrapped error, this will
    70  // check the err itself.
    71  func ContainsType(err error, v interface{}) bool {
    72  	return len(GetAllType(err, v)) > 0
    73  }
    74  
    75  // Get is the same as GetAll but returns the deepest matching error.
    76  func Get(err error, msg string) error {
    77  	es := GetAll(err, msg)
    78  	if len(es) > 0 {
    79  		return es[len(es)-1]
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  // GetType is the same as GetAllType but returns the deepest matching error.
    86  func GetType(err error, v interface{}) error {
    87  	es := GetAllType(err, v)
    88  	if len(es) > 0 {
    89  		return es[len(es)-1]
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // GetAll gets all the errors that might be wrapped in err with the
    96  // given message. The order of the errors is such that the outermost
    97  // matching error (the most recent wrap) is index zero, and so on.
    98  func GetAll(err error, msg string) []error {
    99  	var result []error
   100  
   101  	Walk(err, func(err error) {
   102  		if err.Error() == msg {
   103  			result = append(result, err)
   104  		}
   105  	})
   106  
   107  	return result
   108  }
   109  
   110  // GetAllType gets all the errors that are the same type as v.
   111  //
   112  // The order of the return value is the same as described in GetAll.
   113  func GetAllType(err error, v interface{}) []error {
   114  	var result []error
   115  
   116  	var search string
   117  	if v != nil {
   118  		search = reflect.TypeOf(v).String()
   119  	}
   120  	Walk(err, func(err error) {
   121  		var needle string
   122  		if err != nil {
   123  			needle = reflect.TypeOf(err).String()
   124  		}
   125  
   126  		if needle == search {
   127  			result = append(result, err)
   128  		}
   129  	})
   130  
   131  	return result
   132  }
   133  
   134  // Walk walks all the wrapped errors in err and calls the callback. If
   135  // err isn't a wrapped error, this will be called once for err. If err
   136  // is a wrapped error, the callback will be called for both the wrapper
   137  // that implements error as well as the wrapped error itself.
   138  func Walk(err error, cb WalkFunc) {
   139  	if err == nil {
   140  		return
   141  	}
   142  
   143  	switch e := err.(type) {
   144  	case *wrappedError:
   145  		cb(e.Outer)
   146  		Walk(e.Inner, cb)
   147  	case Wrapper:
   148  		cb(err)
   149  
   150  		for _, err := range e.WrappedErrors() {
   151  			Walk(err, cb)
   152  		}
   153  	case interface{ Unwrap() error }:
   154  		cb(err)
   155  		Walk(e.Unwrap(), cb)
   156  	default:
   157  		cb(err)
   158  	}
   159  }
   160  
   161  // wrappedError is an implementation of error that has both the
   162  // outer and inner errors.
   163  type wrappedError struct {
   164  	Outer error
   165  	Inner error
   166  }
   167  
   168  func (w *wrappedError) Error() string {
   169  	return w.Outer.Error()
   170  }
   171  
   172  func (w *wrappedError) WrappedErrors() []error {
   173  	return []error{w.Outer, w.Inner}
   174  }
   175  
   176  func (w *wrappedError) Unwrap() error {
   177  	return w.Inner
   178  }
   179  

View as plain text