github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/juju/errors/functions.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the LGPLv3, see LICENCE file for details.
     3  
     4  package errors
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  )
    10  
    11  // New is a drop in replacement for the standard libary errors module that records
    12  // the location that the error is created.
    13  //
    14  // For example:
    15  //    return errors.New("validation failed")
    16  //
    17  func New(message string) error {
    18  	err := &Err{message: message}
    19  	err.SetLocation(1)
    20  	return err
    21  }
    22  
    23  // Errorf creates a new annotated error and records the location that the
    24  // error is created.  This should be a drop in replacement for fmt.Errorf.
    25  //
    26  // For example:
    27  //    return errors.Errorf("validation failed: %s", message)
    28  //
    29  func Errorf(format string, args ...interface{}) error {
    30  	err := &Err{message: fmt.Sprintf(format, args...)}
    31  	err.SetLocation(1)
    32  	return err
    33  }
    34  
    35  // Trace adds the location of the Trace call to the stack.  The Cause of the
    36  // resulting error is the same as the error parameter.  If the other error is
    37  // nil, the result will be nil.
    38  //
    39  // For example:
    40  //   if err := SomeFunc(); err != nil {
    41  //       return errors.Trace(err)
    42  //   }
    43  //
    44  func Trace(other error) error {
    45  	if other == nil {
    46  		return nil
    47  	}
    48  	err := &Err{previous: other, cause: Cause(other)}
    49  	err.SetLocation(1)
    50  	return err
    51  }
    52  
    53  // Annotate is used to add extra context to an existing error. The location of
    54  // the Annotate call is recorded with the annotations. The file, line and
    55  // function are also recorded.
    56  //
    57  // For example:
    58  //   if err := SomeFunc(); err != nil {
    59  //       return errors.Annotate(err, "failed to frombulate")
    60  //   }
    61  //
    62  func Annotate(other error, message string) error {
    63  	if other == nil {
    64  		return nil
    65  	}
    66  	err := &Err{
    67  		previous: other,
    68  		cause:    Cause(other),
    69  		message:  message,
    70  	}
    71  	err.SetLocation(1)
    72  	return err
    73  }
    74  
    75  // Annotatef is used to add extra context to an existing error. The location of
    76  // the Annotate call is recorded with the annotations. The file, line and
    77  // function are also recorded.
    78  //
    79  // For example:
    80  //   if err := SomeFunc(); err != nil {
    81  //       return errors.Annotatef(err, "failed to frombulate the %s", arg)
    82  //   }
    83  //
    84  func Annotatef(other error, format string, args ...interface{}) error {
    85  	if other == nil {
    86  		return nil
    87  	}
    88  	err := &Err{
    89  		previous: other,
    90  		cause:    Cause(other),
    91  		message:  fmt.Sprintf(format, args...),
    92  	}
    93  	err.SetLocation(1)
    94  	return err
    95  }
    96  
    97  // DeferredAnnotatef annotates the given error (when it is not nil) with the given
    98  // format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef
    99  // does nothing. This method is used in a defer statement in order to annotate any
   100  // resulting error with the same message.
   101  //
   102  // For example:
   103  //
   104  //    defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg)
   105  //
   106  func DeferredAnnotatef(err *error, format string, args ...interface{}) {
   107  	if *err == nil {
   108  		return
   109  	}
   110  	newErr := &Err{
   111  		message:  fmt.Sprintf(format, args...),
   112  		cause:    Cause(*err),
   113  		previous: *err,
   114  	}
   115  	newErr.SetLocation(1)
   116  	*err = newErr
   117  }
   118  
   119  // Wrap changes the Cause of the error. The location of the Wrap call is also
   120  // stored in the error stack.
   121  //
   122  // For example:
   123  //   if err := SomeFunc(); err != nil {
   124  //       newErr := &packageError{"more context", private_value}
   125  //       return errors.Wrap(err, newErr)
   126  //   }
   127  //
   128  func Wrap(other, newDescriptive error) error {
   129  	err := &Err{
   130  		previous: other,
   131  		cause:    newDescriptive,
   132  	}
   133  	err.SetLocation(1)
   134  	return err
   135  }
   136  
   137  // Wrapf changes the Cause of the error, and adds an annotation. The location
   138  // of the Wrap call is also stored in the error stack.
   139  //
   140  // For example:
   141  //   if err := SomeFunc(); err != nil {
   142  //       return errors.Wrapf(err, simpleErrorType, "invalid value %q", value)
   143  //   }
   144  //
   145  func Wrapf(other, newDescriptive error, format string, args ...interface{}) error {
   146  	err := &Err{
   147  		message:  fmt.Sprintf(format, args...),
   148  		previous: other,
   149  		cause:    newDescriptive,
   150  	}
   151  	err.SetLocation(1)
   152  	return err
   153  }
   154  
   155  // Mask masks the given error with the given format string and arguments (like
   156  // fmt.Sprintf), returning a new error that maintains the error stack, but
   157  // hides the underlying error type.  The error string still contains the full
   158  // annotations. If you want to hide the annotations, call Wrap.
   159  func Maskf(other error, format string, args ...interface{}) error {
   160  	if other == nil {
   161  		return nil
   162  	}
   163  	err := &Err{
   164  		message:  fmt.Sprintf(format, args...),
   165  		previous: other,
   166  	}
   167  	err.SetLocation(1)
   168  	return err
   169  }
   170  
   171  // Mask hides the underlying error type, and records the location of the masking.
   172  func Mask(other error) error {
   173  	if other == nil {
   174  		return nil
   175  	}
   176  	err := &Err{
   177  		previous: other,
   178  	}
   179  	err.SetLocation(1)
   180  	return err
   181  }
   182  
   183  // Cause returns the cause of the given error.  This will be either the
   184  // original error, or the result of a Wrap or Mask call.
   185  //
   186  // Cause is the usual way to diagnose errors that may have been wrapped by
   187  // the other errors functions.
   188  func Cause(err error) error {
   189  	var diag error
   190  	if err, ok := err.(causer); ok {
   191  		diag = err.Cause()
   192  	}
   193  	if diag != nil {
   194  		return diag
   195  	}
   196  	return err
   197  }
   198  
   199  type causer interface {
   200  	Cause() error
   201  }
   202  
   203  type wrapper interface {
   204  	// Message returns the top level error message,
   205  	// not including the message from the Previous
   206  	// error.
   207  	Message() string
   208  
   209  	// Underlying returns the Previous error, or nil
   210  	// if there is none.
   211  	Underlying() error
   212  }
   213  
   214  type locationer interface {
   215  	Location() (string, int)
   216  }
   217  
   218  var (
   219  	_ wrapper    = (*Err)(nil)
   220  	_ locationer = (*Err)(nil)
   221  	_ causer     = (*Err)(nil)
   222  )
   223  
   224  // Details returns information about the stack of errors wrapped by err, in
   225  // the format:
   226  //
   227  // 	[{filename:99: error one} {otherfile:55: cause of error one}]
   228  //
   229  // This is a terse alternative to ErrorStack as it returns a single line.
   230  func Details(err error) string {
   231  	if err == nil {
   232  		return "[]"
   233  	}
   234  	var s []byte
   235  	s = append(s, '[')
   236  	for {
   237  		s = append(s, '{')
   238  		if err, ok := err.(locationer); ok {
   239  			file, line := err.Location()
   240  			if file != "" {
   241  				s = append(s, fmt.Sprintf("%s:%d", file, line)...)
   242  				s = append(s, ": "...)
   243  			}
   244  		}
   245  		if cerr, ok := err.(wrapper); ok {
   246  			s = append(s, cerr.Message()...)
   247  			err = cerr.Underlying()
   248  		} else {
   249  			s = append(s, err.Error()...)
   250  			err = nil
   251  		}
   252  		s = append(s, '}')
   253  		if err == nil {
   254  			break
   255  		}
   256  		s = append(s, ' ')
   257  	}
   258  	s = append(s, ']')
   259  	return string(s)
   260  }
   261  
   262  // ErrorStack returns a string representation of the annotated error. If the
   263  // error passed as the parameter is not an annotated error, the result is
   264  // simply the result of the Error() method on that error.
   265  //
   266  // If the error is an annotated error, a multi-line string is returned where
   267  // each line represents one entry in the annotation stack. The full filename
   268  // from the call stack is used in the output.
   269  //
   270  //     first error
   271  //     yougam/libraries/juju/errors/annotation_test.go:193:
   272  //     yougam/libraries/juju/errors/annotation_test.go:194: annotation
   273  //     yougam/libraries/juju/errors/annotation_test.go:195:
   274  //     yougam/libraries/juju/errors/annotation_test.go:196: more context
   275  //     yougam/libraries/juju/errors/annotation_test.go:197:
   276  func ErrorStack(err error) string {
   277  	return strings.Join(errorStack(err), "\n")
   278  }
   279  
   280  func errorStack(err error) []string {
   281  	if err == nil {
   282  		return nil
   283  	}
   284  
   285  	// We want the first error first
   286  	var lines []string
   287  	for {
   288  		var buff []byte
   289  		if err, ok := err.(locationer); ok {
   290  			file, line := err.Location()
   291  			// Strip off the leading GOPATH/src path elements.
   292  			file = trimGoPath(file)
   293  			if file != "" {
   294  				buff = append(buff, fmt.Sprintf("%s:%d", file, line)...)
   295  				buff = append(buff, ": "...)
   296  			}
   297  		}
   298  		if cerr, ok := err.(wrapper); ok {
   299  			message := cerr.Message()
   300  			buff = append(buff, message...)
   301  			// If there is a cause for this error, and it is different to the cause
   302  			// of the underlying error, then output the error string in the stack trace.
   303  			var cause error
   304  			if err1, ok := err.(causer); ok {
   305  				cause = err1.Cause()
   306  			}
   307  			err = cerr.Underlying()
   308  			if cause != nil && !sameError(Cause(err), cause) {
   309  				if message != "" {
   310  					buff = append(buff, ": "...)
   311  				}
   312  				buff = append(buff, cause.Error()...)
   313  			}
   314  		} else {
   315  			buff = append(buff, err.Error()...)
   316  			err = nil
   317  		}
   318  		lines = append(lines, string(buff))
   319  		if err == nil {
   320  			break
   321  		}
   322  	}
   323  	// reverse the lines to get the original error, which was at the end of
   324  	// the list, back to the start.
   325  	var result []string
   326  	for i := len(lines); i > 0; i-- {
   327  		result = append(result, lines[i-1])
   328  	}
   329  	return result
   330  }