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

     1  package errors
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"strings"
     9  )
    10  
    11  type causes2 struct {
    12  	Code
    13  
    14  	Causers []error
    15  	msg     string
    16  
    17  	unwrapIndex  int // simple index for iterating Unwrap
    18  	maxStringLen int // the output string max-length for an object (see also sites/taggedSites), negative or zero means no limit.
    19  
    20  	liveArgs []interface{} //nolint:revive // error message template ?
    21  }
    22  
    23  func (w *causes2) limitObj(obj interface{}) (s string) { //nolint:revive
    24  	s = fmt.Sprintf("%+v", obj)
    25  	if w.maxStringLen > 0 && len(s) > w.maxStringLen {
    26  		s = s[0:w.maxStringLen-3] + "..."
    27  	}
    28  	return
    29  }
    30  
    31  // WithMaxObjectStringLength for error interface
    32  func (w *causes2) WithMaxObjectStringLength(maxlen int) *causes2 {
    33  	w.maxStringLen = maxlen
    34  	return w
    35  }
    36  
    37  // WithCode for error interface
    38  func (w *causes2) WithCode(code Code) *causes2 {
    39  	w.Code = code
    40  	return w
    41  }
    42  
    43  func (w *causes2) WithMessage(message string, args ...interface{}) *causes2 { //nolint:revive
    44  	if len(args) > 0 {
    45  		message = fmt.Sprintf(message, args...) //nolint:revive
    46  	}
    47  	w.msg = message
    48  	return w
    49  }
    50  
    51  // End ends the WithXXX stream calls while you dislike unwanted `err =`.
    52  func (w *causes2) End() {}
    53  
    54  // Defer can be used as a defer function to simplify your codes.
    55  //
    56  // The codes:
    57  //
    58  //	 func some(){
    59  //	   // as a inner errors container
    60  //	   child := func() (err error) {
    61  //	  	errContainer := errors.New("")
    62  //	  	defer errContainer.Defer(&err)
    63  //
    64  //	  	for _, r := range []error{io.EOF, io.ErrClosedPipe, errors.Internal} {
    65  //	  		errContainer.Attach(r)
    66  //	  	}
    67  //
    68  //	  	return
    69  //	   }
    70  //
    71  //	   err := child()
    72  //	   t.Logf("failed: %+v", err)
    73  //	}
    74  func (w *causes2) Defer(err *error) { //nolint:gocritic
    75  	if w.IsEmpty() {
    76  		*err = nil
    77  	} else {
    78  		*err = w
    79  	}
    80  }
    81  
    82  func (w *causes2) Clear() Container {
    83  	w.msg = ""
    84  	w.Causers = nil
    85  	w.liveArgs = nil
    86  	w.Code = OK
    87  	w.unwrapIndex = 0
    88  	w.maxStringLen = 0
    89  	return w
    90  }
    91  
    92  // WithErrors appends errs
    93  //
    94  // WithStackInfo.Attach() can only wrap and hold one child error object.
    95  //
    96  // WithErrors attach child errors into an error container.
    97  // For a container which has IsEmpty() interface, it would not
    98  // be attached if it is empty (i.e. no errors).
    99  //
   100  // For a nil error object, it will be ignored.
   101  func (w *causes2) WithErrors(errs ...error) *causes2 { //nolint:revive
   102  	for _, e := range errs {
   103  		if e != nil {
   104  			if check, ok := e.(interface{ IsEmpty() bool }); ok {
   105  				if !check.IsEmpty() {
   106  					w.Causers = append(w.Causers, e)
   107  				} else if e.Error() != "" {
   108  					w.Causers = append(w.Causers, e)
   109  				}
   110  			} else {
   111  				w.Causers = append(w.Causers, e)
   112  			}
   113  		}
   114  	}
   115  	return w
   116  }
   117  
   118  // Attach collects the errors except it's nil
   119  func (w *causes2) Attach(errs ...error) {
   120  	// _ = w.WithErrors(errs...)
   121  
   122  	for _, e := range errs {
   123  		if e != nil {
   124  			w.Causers = append(w.Causers, e)
   125  		}
   126  	}
   127  }
   128  
   129  // FormatWith _
   130  func (w *causes2) FormatWith(args ...interface{}) error { //nolint:revive
   131  	c := w.Clone()
   132  	c.liveArgs = args
   133  	return c
   134  }
   135  
   136  // Clone _
   137  func (w *causes2) Clone() *causes2 {
   138  	c := &causes2{
   139  		Code:        w.Code,
   140  		Causers:     w.Causers,
   141  		msg:         w.msg,
   142  		unwrapIndex: w.unwrapIndex,
   143  		liveArgs:    w.liveArgs,
   144  	}
   145  	return c
   146  }
   147  
   148  func (w *causes2) Error() string {
   149  	return w.makeErrorString(false)
   150  }
   151  
   152  func (w *causes2) makeErrorString(line bool) string { //nolint:revive
   153  	var buf bytes.Buffer
   154  	if w.msg != "" {
   155  		if len(w.liveArgs) > 0 {
   156  			msg := fmt.Sprintf(w.msg, w.liveArgs...)
   157  			_, _ = buf.WriteString(msg)
   158  		} else {
   159  			_, _ = buf.WriteString(w.msg)
   160  		}
   161  	}
   162  
   163  	if line {
   164  		_, _ = buf.WriteRune('\n')
   165  		if w.Code != OK {
   166  			_, _ = buf.WriteString(w.Code.String())
   167  			_, _ = buf.WriteRune('\n')
   168  		}
   169  
   170  		for _, c := range w.Causers {
   171  			_, _ = buf.WriteString("  - ")
   172  			var xc *causes2
   173  			if As(c, &xc) {
   174  				_, _ = buf.WriteString(leftPad(xc.makeErrorString(line), "  ", false))
   175  			} else {
   176  				_, _ = buf.WriteString(leftPad(c.Error(), "    ", false))
   177  			}
   178  		}
   179  
   180  		// buf.WriteRune('\n')
   181  		return buf.String()
   182  	}
   183  
   184  	var needclose, needsep bool
   185  	if w.Code != OK {
   186  		if buf.Len() > 0 {
   187  			_, _ = buf.WriteRune(' ')
   188  		}
   189  		_, _ = buf.WriteString("[")
   190  		_, _ = buf.WriteString(w.Code.String())
   191  		needclose = true
   192  		needsep = true
   193  	}
   194  	if w.msg == "" {
   195  		needsep = false
   196  	}
   197  	if len(w.Causers) > 0 {
   198  		if buf.Len() > 0 {
   199  			_, _ = buf.WriteRune(' ')
   200  		}
   201  		_, _ = buf.WriteString("[")
   202  		needclose = true
   203  	}
   204  
   205  	for i, c := range w.Causers {
   206  		if i > 0 || needsep {
   207  			_, _ = buf.WriteString(" | ")
   208  		}
   209  		_, _ = buf.WriteString(c.Error())
   210  	}
   211  	if needclose {
   212  		_, _ = buf.WriteString("]")
   213  	}
   214  	// buf.WriteString(w.Stack)
   215  	return buf.String()
   216  }
   217  
   218  func leftPad(s, padStr string, firstLine bool) string { //nolint:revive
   219  	if padStr == "" {
   220  		return s
   221  	}
   222  
   223  	var ln int
   224  	var sb strings.Builder
   225  	scanner := bufio.NewScanner(bufio.NewReader(strings.NewReader(s)))
   226  	for scanner.Scan() {
   227  		if ln != 0 || firstLine {
   228  			_, _ = sb.WriteString(padStr)
   229  		}
   230  		_, _ = sb.WriteString(scanner.Text())
   231  		_, _ = sb.WriteRune('\n')
   232  		ln++
   233  	}
   234  	return sb.String()
   235  }
   236  
   237  // Format formats the stack of Frames according to the fmt.Formatter interface.
   238  //
   239  //	%s	lists source files for each Frame in the stack
   240  //	%v	lists the source file and line number for each Frame in the stack
   241  //
   242  // Format accepts flags that alter the printing of some verbs, as follows:
   243  //
   244  //	%+v   Prints filename, function, and line number for each Frame in the stack.
   245  func (w *causes2) Format(s fmt.State, verb rune) {
   246  	switch verb {
   247  	case 'v':
   248  		if s.Flag('+') {
   249  			_, _ = fmt.Fprintf(s, "%+v", w.makeErrorString(true))
   250  			return
   251  		}
   252  		fallthrough
   253  	case 's':
   254  		_, _ = io.WriteString(s, w.Error())
   255  	case 'q':
   256  		_, _ = fmt.Fprintf(s, "%q", w.Error())
   257  	}
   258  }
   259  
   260  // String for stringer interface
   261  func (w *causes2) String() string { return w.Error() }
   262  
   263  func (w *causes2) Cause() error {
   264  	if len(w.Causers) == 0 {
   265  		return nil
   266  	}
   267  	return w.Causers[0]
   268  }
   269  
   270  // Causes simply returns the wrapped inner errors.
   271  // It doesn't consider an wrapped Code entity is an inner error too.
   272  // So if you wanna to extract any inner error objects, use
   273  // errors.Unwrap for instead. The errors.Unwrap could extract all
   274  // of them one by one:
   275  //
   276  //	var err = errors.New("hello").WithErrors(io.EOF, io.ShortBuffers)
   277  //	var e error = err
   278  //	for e != nil {
   279  //	    e = errors.Unwrap(err)
   280  //	}
   281  func (w *causes2) Causes() []error {
   282  	if len(w.Causers) == 0 {
   283  		return nil
   284  	}
   285  	return w.Causers
   286  }
   287  
   288  func (w *causes2) Unwrap() error {
   289  	defer func() { w.unwrapIndex++ }()
   290  
   291  	if w.unwrapIndex >= 0 && w.unwrapIndex < len(w.Causers) {
   292  		return w.Causers[w.unwrapIndex]
   293  	}
   294  	if w.unwrapIndex == len(w.Causers) {
   295  		if w.Code != OK {
   296  			return w.Code
   297  		}
   298  	}
   299  
   300  	// reset index
   301  	w.unwrapIndex = -1
   302  	return nil
   303  }
   304  
   305  func (w *causes2) Reset() {
   306  	w.unwrapIndex = 0
   307  }
   308  
   309  // IsDescended test if ancestor is an error template and descendant
   310  // is derived from it by calling ancestor.FormatWith.
   311  func IsDescended(ancestor, descendant error) bool {
   312  	if a, ok := ancestor.(interface{ IsDescended(descendant error) bool }); ok {
   313  		return a.IsDescended(descendant)
   314  	}
   315  	return false
   316  }
   317  
   318  func (w *causes2) IsDescended(descendant error) bool {
   319  	if e, ok := descendant.(*causes2); ok {
   320  		return e.Code == w.Code && e.msg == w.msg
   321  	}
   322  	return false
   323  }
   324  
   325  func (w *causes2) Is(target error) bool {
   326  	if w.Code != OK {
   327  		if c, ok := target.(Code); ok && c == w.Code {
   328  			return true
   329  		}
   330  	}
   331  	if IsSlice(w.Causers, target) {
   332  		return true
   333  	}
   334  	if te, ok := target.(*causes2); ok {
   335  		return w.equal(te)
   336  	}
   337  	return false
   338  }
   339  
   340  func (w *causes2) equal(target *causes2) bool {
   341  	return w.Code == target.Code &&
   342  		w.msg == target.msg &&
   343  		w.arrayEqual(w.Causers, target.Causers)
   344  }
   345  
   346  func (w *causes2) arrayEqual(a, b []error) bool {
   347  	if len(a) != len(b) {
   348  		return false
   349  	}
   350  	yes := true
   351  	for i := 0; i < len(a); i++ {
   352  		if Is(a[i], b[i]) {
   353  			yes = false
   354  			break
   355  		}
   356  	}
   357  	return yes
   358  }
   359  
   360  func (w *causes2) TypeIs(target error) bool {
   361  	return TypeIsSlice(w.Causers, target)
   362  }
   363  
   364  // As finds the first error in `err`'s chain that matches target,
   365  // and if so, sets target to that error value and returns true.
   366  func (w *causes2) As(target interface{}) bool { //nolint:revive
   367  	if c, ok := target.(*Code); ok {
   368  		*c = w.Code
   369  		return true
   370  	}
   371  	if c, ok := target.(*[]error); ok {
   372  		*c = w.Causers
   373  		return true
   374  	}
   375  	if c, ok := target.(**causes2); ok {
   376  		*c = w
   377  		return true
   378  	}
   379  	return AsSlice(w.Causers, target)
   380  }
   381  
   382  // IsEmpty tests has attached errors
   383  func (w *causes2) IsEmpty() bool {
   384  	return w.Code == OK && len(w.Causers) == 0 && len(w.liveArgs) == 0
   385  }