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

     1  package errors
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"reflect"
     7  	"strings"
     8  )
     9  
    10  // WithStackInfo is exported now
    11  type WithStackInfo struct {
    12  	causes2
    13  
    14  	*Stack
    15  
    16  	sites       []interface{}          //nolint:revive
    17  	taggedSites map[string]interface{} //nolint:revive
    18  }
    19  
    20  func (w *WithStackInfo) IsDescended(descendant error) bool {
    21  	if e, ok := descendant.(*WithStackInfo); ok {
    22  		return e.Code == w.Code && e.msg == w.msg
    23  	}
    24  	return false
    25  }
    26  
    27  // String for stringer interface
    28  func (w *WithStackInfo) String() string { return w.Error() }
    29  
    30  // WithStack annotates err with a Stack trace at the point WithStack
    31  // was called.
    32  //
    33  // If err is nil, WithStack returns nil.
    34  func WithStack(cause error) error {
    35  	return withStackZ(cause)
    36  }
    37  
    38  func withStackZ(cause error) *WithStackInfo {
    39  	if cause == nil {
    40  		return nil
    41  	}
    42  	return &WithStackInfo{causes2: causes2{Causers: []error{cause}}, Stack: callers(1)}
    43  }
    44  
    45  // End ends the WithXXX stream calls while you dislike unwanted `err =`.
    46  //
    47  // For instance, the construction of an error without warnings looks like:
    48  //
    49  //	err := New("hello %v", "world")
    50  //	_ = err.
    51  //	    WithErrors(io.EOF, io.ErrShortWrite).
    52  //	    WithErrors(io.ErrClosedPipe).
    53  //	    WithCode(Internal)
    54  //
    55  // To avoid the `_ =`, you might beloved with a End() call:
    56  //
    57  //	err := New("hello %v", "world")
    58  //	err.WithErrors(io.EOF, io.ErrShortWrite).
    59  //	    WithErrors(io.ErrClosedPipe).
    60  //	    WithCode(Internal).
    61  //	    End()
    62  func (w *WithStackInfo) End() {}
    63  
    64  // Data returns the wrapped common user data by WithData.
    65  // The error objects with passed WithData will be moved into inner
    66  // errors set, so its are excluded from Data().
    67  func (w *WithStackInfo) Data() []interface{} { return w.sites } //nolint:revive
    68  
    69  // TaggedData returns the wrapped tagged user data by WithTaggedData.
    70  func (w *WithStackInfo) TaggedData() TaggedData { return w.taggedSites }
    71  
    72  // Cause returns the underlying cause of the error, if possible.
    73  // An error value has a cause if it implements the following
    74  // interface:
    75  //
    76  //	type causer interface {
    77  //	       Cause() error
    78  //	}
    79  //
    80  // If an error object does not implement Cause interface, the
    81  // original error object will be returned.
    82  // If the error is nil, nil will be returned without further
    83  // investigation.
    84  func (w *WithStackInfo) Cause() error {
    85  	return w.causes2.Cause()
    86  }
    87  
    88  func (w *WithStackInfo) rebuild() Buildable {
    89  	return w
    90  }
    91  
    92  // WithSkip specifies a special number of stack frames that will
    93  // be ignored.
    94  func (w *WithStackInfo) WithSkip(skip int) Buildable {
    95  	w.Stack = callers(skip)
    96  	return w
    97  }
    98  
    99  // WithMessage formats the error message
   100  func (w *WithStackInfo) WithMessage(message string, args ...interface{}) Buildable { //nolint:revive
   101  	_ = w.causes2.WithMessage(message, args...)
   102  	return w
   103  }
   104  
   105  // WithCode specifies an error code.
   106  // An error code `Code` is a integer number with error interface
   107  // supported.
   108  func (w *WithStackInfo) WithCode(code Code) Buildable {
   109  	w.Code = code
   110  	return w.rebuild()
   111  }
   112  
   113  // Attach collects the errors except an error is nil.
   114  //
   115  // StackTrace of errs will be copied to callee so that you can get a
   116  // trace output nearer by the last error.
   117  //
   118  // Since v3.0.5, we break Attach() and remove its returning value.
   119  // So WithStackInfo is a Container compliant type now.
   120  func (w *WithStackInfo) Attach(errs ...error) {
   121  	// _ = w.WithErrors(errs...)
   122  
   123  	for _, e := range errs {
   124  		if e != nil {
   125  			w.Causers = append(w.Causers, e)
   126  		}
   127  	}
   128  
   129  	for _, e := range errs {
   130  		if e1, ok := e.(*WithStackInfo); ok {
   131  			w.Stack = e1.Stack
   132  		}
   133  	}
   134  }
   135  
   136  // WithErrors attaches the given errs as inner errors.
   137  //
   138  // WithErrors is similar with Attach() except it collects
   139  // thees errors:
   140  //
   141  // 1. For an error implemented IsEmpty(), only if it is
   142  // not empty (i.e. IsEmpty() return false). So the inner
   143  // errors within an error container will be moved out
   144  // from that container, and be copied into this holder.
   145  //
   146  // 2. For a normal error, such as io.EOF, just add it.
   147  //
   148  // It wraps the inner errors into underlying container and
   149  // represents them all in a singular up-level error object.
   150  // The wrapped inner errors can be retrieved with errors.Causes:
   151  //
   152  //	var err = errors.New("hello").WithErrors(io.EOF, io.ShortBuffers)
   153  //	var errs []error = errors.Causes(err)
   154  //
   155  // Or, use As() to extract its:
   156  //
   157  //	var errs []error
   158  //	errors.As(err, &errs)
   159  func (w *WithStackInfo) WithErrors(errs ...error) Buildable {
   160  	_ = w.causes2.WithErrors(errs...)
   161  
   162  	// for _, e := range errs {
   163  	//	if e1, ok := e.(*WithStackInfo); ok {
   164  	//		w.Stack = e1.Stack
   165  	//	}
   166  	// }
   167  	return w
   168  }
   169  
   170  // WithData appends errs if the general object is a error object.
   171  //
   172  // StackTrace of errs will be copied to callee so that you can get a
   173  // trace output nearer by the last error.
   174  //
   175  // defer-recover block typically is a better place of WithData().
   176  //
   177  // For example:
   178  //
   179  //	defer func() {
   180  //	  if e := recover(); e != nil {
   181  //	    err = errors.New("[recovered] copyTo unsatisfied ([%v] %v -> [%v] %v), causes: %v",
   182  //	      c.indirectType(from.Type()), from, c.indirectType(to.Type()), to, e).
   183  //	      WithData(e)                 // StackTrace of e -> err
   184  //	    n := log.CalcStackFrames(1)   // skip defer-recover frame at first
   185  //	    log.Skip(n).Errorf("%v", err) // skip go-lib frames and defer-recover frame, back to the point throwing panic
   186  //	  }
   187  //	}()
   188  func (w *WithStackInfo) WithData(errs ...interface{}) Buildable { //nolint:revive
   189  	if len(errs) > 0 {
   190  		for _, e := range errs {
   191  			if e1, ok := e.(error); ok {
   192  				_ = w.WithErrors(e1)
   193  				if e1, ok := e.(*WithStackInfo); ok {
   194  					w.Stack = e1.Stack
   195  				}
   196  			} else if e != nil {
   197  				w.sites = append(w.sites, e)
   198  			}
   199  		}
   200  	}
   201  	return w
   202  }
   203  
   204  // WithTaggedData appends errs if the general object is a error object
   205  func (w *WithStackInfo) WithTaggedData(siteScenes TaggedData) Buildable {
   206  	if w.taggedSites == nil {
   207  		w.taggedSites = make(TaggedData)
   208  	}
   209  	for k, v := range siteScenes {
   210  		w.taggedSites[k] = v
   211  	}
   212  	return w
   213  }
   214  
   215  // WithCause sets the underlying error manually if necessary.
   216  func (w *WithStackInfo) WithCause(cause error) Buildable {
   217  	w.causes2.Causers = append(w.causes2.Causers, cause)
   218  	return w
   219  }
   220  
   221  // WithMaxObjectStringLength set limitation for object stringify length.
   222  //
   223  // The objects of Data/TaggedData will be limited while its' been formatted with "%+v"
   224  func (w *WithStackInfo) WithMaxObjectStringLength(maxlen int) Buildable {
   225  	w.causes2.WithMaxObjectStringLength(maxlen) //nolint:errcheck
   226  	return w
   227  }
   228  
   229  // Defer can be used as a defer function to simplify your codes.
   230  //
   231  // The codes:
   232  //
   233  //	 func some(){
   234  //	   // as a inner errors container
   235  //	   child := func() (err error) {
   236  //	  	errContainer := errors.New("")
   237  //	  	defer errContainer.Defer(&err)
   238  //
   239  //	  	for _, r := range []error{io.EOF, io.ErrClosedPipe, errors.Internal} {
   240  //	  		errContainer.Attach(r)
   241  //	  	}
   242  //
   243  //	  	return
   244  //	   }
   245  //
   246  //	   err := child()
   247  //	   t.Logf("failed: %+v", err)
   248  //	}
   249  func (w *WithStackInfo) Defer(err *error) { //nolint:gocritic
   250  	if w.IsEmpty() {
   251  		*err = nil
   252  	} else {
   253  		*err = w // no inner errors attached into an error container, that assumed 'is empty'
   254  	}
   255  }
   256  
   257  func (w *WithStackInfo) Clear() Container {
   258  	w.msg = ""
   259  	w.sites = nil
   260  	w.taggedSites = nil
   261  	w.Causers = nil
   262  	w.liveArgs = nil
   263  	return w
   264  }
   265  
   266  // IsEmpty tests has attached errors
   267  func (w *WithStackInfo) IsEmpty() bool {
   268  	return len(w.sites) == 0 && len(w.taggedSites) == 0 && w.causes2.IsEmpty()
   269  }
   270  
   271  // FormatWith _
   272  func (w *WithStackInfo) FormatWith(args ...interface{}) error { //nolint:revive
   273  	c := w.Clone()
   274  	c.liveArgs = args
   275  	return c
   276  }
   277  
   278  // Clone _
   279  func (w *WithStackInfo) Clone() *WithStackInfo {
   280  	c := &WithStackInfo{
   281  		causes2: causes2{
   282  			Code:        w.causes2.Code,
   283  			Causers:     w.causes2.Causers,
   284  			msg:         w.causes2.msg,
   285  			unwrapIndex: w.causes2.unwrapIndex,
   286  			liveArgs:    w.causes2.liveArgs,
   287  		},
   288  		Stack:       w.Stack,
   289  		sites:       w.sites,
   290  		taggedSites: w.taggedSites,
   291  	}
   292  	return c
   293  }
   294  
   295  func snfmt(sb *strings.Builder, format string, args ...interface{}) (n int) { //nolint:revive
   296  	str := fmt.Sprintf(format, args...)
   297  	// n = len(str)
   298  	n, _ = sb.WriteString(str)
   299  	return
   300  }
   301  
   302  // Format formats the stack of Frames according to the fmt.Formatter interface.
   303  //
   304  //	%s   lists source files for each Frame in the stack
   305  //	%v   lists the source file and line number for each Frame in the stack
   306  //
   307  // Format accepts flags that alter the printing of some verbs, as follows:
   308  //
   309  //	%+v   Prints filename, function, and line number for each Frame in the stack.
   310  func (w *WithStackInfo) Format(s fmt.State, verb rune) { //nolint:revive
   311  	switch verb {
   312  	case 'v':
   313  		if s.Flag('+') {
   314  			var sb strings.Builder
   315  			n := snfmt(&sb, "%+v", w.makeErrorString(true))
   316  			if len(w.sites) > 0 {
   317  				if n > 0 {
   318  					n += snfmt(&sb, "\n  ")
   319  				}
   320  				// n += snfmt(&sb, "Sites: %+v", w.sites)
   321  				n += snfmt(&sb, "Sites:\n")
   322  				for i, site := range w.sites {
   323  					n += snfmt(&sb, "    %d. %+v\n", i+1, w.limitObj(site))
   324  				}
   325  			}
   326  			if len(w.taggedSites) > 0 {
   327  				if n > 0 {
   328  					n += snfmt(&sb, "\n  ")
   329  				}
   330  				// snfmt(&sb, "Tagged Sites: %+v", w.taggedSites)
   331  				n += snfmt(&sb, "Tagged Sites:\n")
   332  				for k, site := range w.taggedSites {
   333  					n += snfmt(&sb, "    %v => %+v\n", k, w.limitObj(site))
   334  				}
   335  			}
   336  			_, _ = fmt.Fprint(s, sb.String())
   337  			w.Stack.Format(s, verb)
   338  			return
   339  		}
   340  		_, _ = fmt.Fprintf(s, "%v", w.Error())
   341  	case 's':
   342  		_, _ = io.WriteString(s, w.Error())
   343  	case 'q':
   344  		_, _ = fmt.Fprintf(s, "%q", w.Error())
   345  	}
   346  }
   347  
   348  // Is reports whether any error in `err`'s chain matches target.
   349  func (w *WithStackInfo) Is(target error) bool {
   350  	if te, ok := target.(*WithStackInfo); ok {
   351  		return w.equal(te)
   352  	}
   353  	for _, e := range w.Causers {
   354  		if Is(e, target) {
   355  			return true
   356  		}
   357  	}
   358  	return w.causes2.Is(target)
   359  }
   360  
   361  func (w *WithStackInfo) equal(target *WithStackInfo) bool {
   362  	if w.causes2.equal(&target.causes2) &&
   363  		reflect.DeepEqual(w.sites, target.sites) &&
   364  		reflect.DeepEqual(w.taggedSites, target.taggedSites) {
   365  		return true
   366  	}
   367  
   368  	for _, e := range w.Causers {
   369  		if Is(target, e) {
   370  			return true
   371  		}
   372  	}
   373  
   374  	return false
   375  }
   376  
   377  // // TypeIs reports whether any error in `err`'s chain matches target.
   378  // func (w *WithStackInfo) TypeIs(target error) bool {
   379  //	if x, ok := w.error.(interface{ TypeIs(error) bool }); ok && x.TypeIs(target) {
   380  //		return true
   381  //	}
   382  //	return w.error == target
   383  // }
   384  
   385  // // As finds the first error in `err`'s chain that matches target, and if so, sets
   386  // // target to that error value and returns true.
   387  // func (w *WithStackInfo) As(target interface{}) bool {
   388  //	return As(w.error, target)
   389  //	//if target == nil {
   390  //	//	panic("errors: target cannot be nil")
   391  //	//}
   392  //	//val := reflect.ValueOf(target)
   393  //	//typ := val.Type()
   394  //	//if typ.Kind() != reflect.Ptr || val.IsNil() {
   395  //	//	panic("errors: target must be a non-nil pointer")
   396  //	//}
   397  //	//if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
   398  //	//	panic("errors: *target must be interface or implement error")
   399  //	//}
   400  //	//targetType := typ.Elem()
   401  //	//err := w.error
   402  //	//for err != nil {
   403  //	//	if reflect.TypeOf(err).AssignableTo(targetType) {
   404  //	//		val.Elem().Set(reflect.ValueOf(err))
   405  //	//		return true
   406  //	//	}
   407  //	//	if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
   408  //	//		return true
   409  //	//	}
   410  //	//	err = Unwrap(err)
   411  //	//}
   412  //	//return false
   413  // }
   414  
   415  // // Unwrap returns the result of calling the Unwrap method on err, if
   416  // // `err`'s type contains an Unwrap method returning error.
   417  // // Otherwise, Unwrap returns nil.
   418  // func (w *WithStackInfo) Unwrap() error {
   419  //	if w.error != nil {
   420  //		return w.error
   421  //	}
   422  //	//if x, ok := w.error.(interface{ Unwrap() error }); ok {
   423  //	//	return x.Unwrap()
   424  //	//}
   425  //	return nil
   426  // }
   427  
   428  // // IsEmpty tests has attached errors
   429  // func (w *WithStackInfo) IsEmpty() bool {
   430  //	if x, ok := w.error.(interface{ IsEmpty() bool }); ok {
   431  //		return x.IsEmpty()
   432  //	}
   433  //	return false
   434  // }