gopkg.in/hedzr/errors.v3@v3.3.1/errors.go (about)

     1  package errors
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  )
     7  
     8  // New returns an error with the supplied message.
     9  // New also records the Stack trace at the point where it was called.
    10  //
    11  // New supports two kind of args: an Opt option or a message format
    12  // with variadic args.
    13  //
    14  // Sample 1:
    15  //
    16  //	var err = errors.New("message here: %s", "hello")
    17  //
    18  // Sample 2:
    19  //
    20  //	var err = errors.New(errors.WithErrors(errs...))
    21  //	var err = errors.New(errors.WithStack(cause))
    22  //
    23  // Sample 3:
    24  //
    25  //	var err = errors.New().WithErrors(errs...)
    26  func New(args ...interface{}) Error { //nolint:revive
    27  	if len(args) > 0 {
    28  		s := &builder{skip: 1}
    29  
    30  		if message, ok := args[0].(string); ok {
    31  			return s.WithSkip(2).WithMessage(message, args[1:]...).Build()
    32  		}
    33  
    34  		for _, opt := range args {
    35  			if o, ok := opt.(Opt); ok {
    36  				o(s)
    37  			}
    38  		}
    39  		return s.Build()
    40  	}
    41  
    42  	return &WithStackInfo{Stack: callers(1)}
    43  }
    44  
    45  // NewLite returns a simple message error object via stdlib (errors.New).
    46  //
    47  // Sample:
    48  //
    49  //	var err1 = errors.New("message") // simple message
    50  //	var err1 = errors.New(errors.WithStack(cause)) // return Error object with Opt
    51  //
    52  // If no send any args as parameters, Code `UnsupportedOperation` returned.
    53  func NewLite(args ...interface{}) error { //nolint:revive
    54  	if len(args) > 0 {
    55  		if message, ok := args[0].(string); ok {
    56  			if len(args) > 1 {
    57  				message = fmt.Sprintf(message, args[1:]...)
    58  			}
    59  			return errors.New(message)
    60  		}
    61  
    62  		s := &builder{skip: 1}
    63  		for _, opt := range args {
    64  			if o, ok := opt.(Opt); ok {
    65  				o(s)
    66  			}
    67  		}
    68  		return s.Build()
    69  	}
    70  	return UnsupportedOperation // errors.ErrUnsupported
    71  }
    72  
    73  // Opt _
    74  type Opt func(s *builder)
    75  
    76  // WithErrors attach child errors into an error container.
    77  // For a container which has IsEmpty() interface, it would not be
    78  // attached if it is empty (i.e. no errors).
    79  // For a nil error object, it will be ignored.
    80  //
    81  // Sample:
    82  //
    83  //	err := errors.New(errors.WithErrors(errs...))
    84  func WithErrors(errs ...error) Opt {
    85  	return func(s *builder) {
    86  		s.WithErrors(errs...)
    87  	}
    88  }
    89  
    90  // Skip sets how many frames will be ignored while we are extracting
    91  // the stacktrace info.
    92  // Skip starts a builder with fluent API style, so you could continue
    93  // build the error what you want:
    94  //
    95  //	err := errors.Skip(1).Message("hello %v", "you").Build()
    96  func Skip(skip int) Builder {
    97  	return &builder{skip: skip}
    98  }
    99  
   100  // Message formats a message and starts a builder to create the final
   101  // error object.
   102  //
   103  //	err := errors.Message("hello %v", "you").Attach(causer).Build()
   104  func Message(message string, args ...interface{}) Builder { //nolint:revive
   105  	return NewBuilder().WithMessage(message, args...)
   106  }
   107  
   108  // NewBuilder starts a new error builder.
   109  //
   110  // Typically, you could make an error with fluent calls:
   111  //
   112  //	err = errors.NewBuilder().
   113  //		WithCode(Internal).
   114  //		WithErrors(io.EOF).
   115  //		WithErrors(io.ErrShortWrite).
   116  //		Build()
   117  //	t.Logf("failed: %+v", err)
   118  func NewBuilder() Builder {
   119  	return &builder{skip: 1}
   120  }
   121  
   122  // Builder provides a fluent calling interface to make error
   123  // building easy.
   124  type Builder interface {
   125  	// WithSkip specifies a special number of stack frames that will
   126  	// be ignored.
   127  	WithSkip(skip int) Builder
   128  	// WithErrors attaches the given errs as inner errors.
   129  	// For a container which has IsEmpty() interface, it would not
   130  	// be attached if it is empty (i.e. no errors).
   131  	// For a nil error object, it will be ignored.
   132  	WithErrors(errs ...error) Builder
   133  	// WithMessage formats the error message
   134  	WithMessage(message string, args ...interface{}) Builder //nolint:revive
   135  	// WithCode specifies an error code.
   136  	WithCode(code Code) Builder
   137  
   138  	// Build builds the final error object (with Buildable interface
   139  	// bound)
   140  	Build() Error
   141  
   142  	// BREAK - Use WithErrors() for instead
   143  	// Attach inner errors for backward compatibility to v2
   144  	// Attach(errs ...error)
   145  }
   146  
   147  type builder struct {
   148  	skip        int
   149  	causes2     causes2
   150  	sites       []interface{} //nolint:revive
   151  	taggedSites TaggedData
   152  }
   153  
   154  // WithSkip specifies a special number of stack frames that will
   155  // be ignored.
   156  func (s *builder) WithSkip(skip int) Builder {
   157  	s.skip = skip
   158  	return s
   159  }
   160  
   161  // WithCode specifies an error code.
   162  func (s *builder) WithCode(code Code) Builder {
   163  	s.causes2.Code = code
   164  	return s
   165  }
   166  
   167  // // Attach attaches the given errs as inner errors.
   168  // // For backward compatibility to v2
   169  // func (s *builder) Attach(errs ...error) Buildable {
   170  //	return s.WithErrors(errs...).Build()
   171  // }
   172  
   173  // WithMessage formats the error message
   174  func (s *builder) WithMessage(message string, args ...interface{}) Builder { //nolint:revive
   175  	if len(args) > 0 {
   176  		message = fmt.Sprintf(message, args...) //nolint:revive
   177  	}
   178  	s.causes2.msg = message
   179  	return s
   180  }
   181  
   182  // WithErrors attaches the given errs as inner errors.
   183  // For a container which has IsEmpty() interface, it would not
   184  // be attached if it is empty (i.e. no errors).
   185  // For a nil error object, it will be ignored.
   186  func (s *builder) WithErrors(errs ...error) Builder {
   187  	_ = s.causes2.WithErrors(errs...)
   188  	return s
   189  }
   190  
   191  // WithData appends errs if the general object is a error object.
   192  // It can be used in defer-recover block typically. For example:
   193  //
   194  //	defer func() {
   195  //	  if e := recover(); e != nil {
   196  //	    err = errors.New("[recovered] copyTo unsatisfied ([%v] %v -> [%v] %v), causes: %v",
   197  //	      c.indirectType(from.Type()), from, c.indirectType(to.Type()), to, e).
   198  //	      WithData(e)
   199  //	    n := log.CalcStackFrames(1)   // skip defer-recover frame at first
   200  //	    log.Skip(n).Errorf("%v", err) // skip go-lib frames and defer-recover frame, back to the point throwing panic
   201  //	  }
   202  //	}()
   203  func (s *builder) WithData(errs ...interface{}) Builder { //nolint:revive
   204  	s.sites = append(s.sites, errs...)
   205  	return s
   206  }
   207  
   208  // WithTaggedData appends user data with tag into internal container.
   209  // These data can be retrieved by
   210  func (s *builder) WithTaggedData(siteScenes TaggedData) Builder {
   211  	if s.taggedSites == nil {
   212  		s.taggedSites = make(TaggedData)
   213  	}
   214  	for k, v := range siteScenes {
   215  		s.taggedSites[k] = v
   216  	}
   217  	return s
   218  }
   219  
   220  // WithCause sets the underlying error manually if necessary.
   221  func (s *builder) WithCause(cause error) Builder {
   222  	_ = s.causes2.WithErrors(cause)
   223  	return s
   224  }
   225  
   226  // Build builds the final error object (with *WithStackInfo type wrapped)
   227  func (s *builder) Build() Error {
   228  	w := &WithStackInfo{
   229  		causes2: s.causes2,
   230  		Stack:   callers(s.skip),
   231  	}
   232  	return w
   233  }