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

     1  package errors
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  )
     8  
     9  // As finds the first error in `err`'s chain that matches target,
    10  // and if so, sets target to that error value and returns true.
    11  //
    12  // The chain consists of err itself followed by the sequence of errors
    13  // obtained by repeatedly calling Unwrap.
    14  //
    15  // An error matches target if the error's concrete value is assignable
    16  // to the value pointed to by target, or if the error has a method
    17  // As(interface{}) bool such that As(target) returns true. In the
    18  // latter case, the As method is responsible for setting target.
    19  //
    20  // As will panic if target is not a non-nil pointer to either a
    21  // type that implements error, or to any interface type. "As"
    22  // returns false if err is nil.
    23  func As(err error, target interface{}) bool { //nolint:revive
    24  	if target == nil {
    25  		panic("errors: target cannot be nil")
    26  	}
    27  	val := reflect.ValueOf(target)
    28  	typ := val.Type()
    29  	if typ.Kind() != reflect.Ptr || val.IsNil() {
    30  		panic("errors: target must be a non-nil pointer")
    31  	}
    32  	// e := typ.Elem()
    33  	// k := e.Kind()
    34  	// if k != reflect.Interface && k != reflect.Slice && !e.Implements(errorType) {
    35  	// 	// panic("errors: *target must be interface or implement error")
    36  	// 	return false
    37  	// }
    38  	targetType := typ.Elem()
    39  	for err != nil {
    40  		if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { //nolint:revive
    41  			return true
    42  		}
    43  		if reflect.TypeOf(err).AssignableTo(targetType) {
    44  			val.Elem().Set(reflect.ValueOf(err))
    45  			return true
    46  		}
    47  		err = Unwrap(err) //nolint:revive
    48  	}
    49  	return false
    50  }
    51  
    52  // AsSlice tests err.As for errs slice
    53  func AsSlice(errs []error, target interface{}) bool { //nolint:revive
    54  	if target == nil {
    55  		panic("errors: target cannot be nil")
    56  	}
    57  	val := reflect.ValueOf(target)
    58  	typ := val.Type()
    59  	if typ.Kind() != reflect.Ptr || val.IsNil() {
    60  		panic("errors: target must be a non-nil pointer")
    61  	}
    62  	if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
    63  		// panic("errors: *target must be interface or implement error")
    64  		return false
    65  	}
    66  	targetType := typ.Elem()
    67  	for _, err := range errs {
    68  		if reflect.TypeOf(err).AssignableTo(targetType) {
    69  			val.Elem().Set(reflect.ValueOf(err))
    70  			return true
    71  		}
    72  		if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { //nolint:revive
    73  			return true
    74  		}
    75  		err = Unwrap(err) //nolint:ineffassign,staticcheck
    76  	}
    77  	return false
    78  }
    79  
    80  var errorType = reflect.TypeOf((*error)(nil)).Elem()
    81  
    82  // IsAnyOf tests whether any of `targets` is in `err`.
    83  func IsAnyOf(err error, targets ...error) bool {
    84  	for _, tgt := range targets {
    85  		if Is(err, tgt) {
    86  			return true
    87  		}
    88  	}
    89  	return false
    90  }
    91  
    92  // Is reports whether any error in `err`'s chain matches target.
    93  //
    94  // The chain consists of err itself followed by the sequence of
    95  // errors obtained by repeatedly calling Unwrap.
    96  //
    97  // An error is considered to match a target if it is equal to that
    98  // target or if it implements a method Is(error) bool such that
    99  // Is(target) returns true.
   100  func Is(err, target error) bool { //nolint:revive
   101  	if target == nil {
   102  		return err == nil
   103  	}
   104  
   105  	isComparable := reflect.TypeOf(target).Comparable()
   106  	tv := reflect.ValueOf(target)
   107  	// target is not Code-based, try convert source err with target's type, and test whether its plain text message is equal
   108  	var savedMsg string
   109  	if !isNil(tv) {
   110  		savedMsg = target.Error()
   111  	}
   112  	for {
   113  		if isComparable && err == target {
   114  			return true
   115  		}
   116  		if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
   117  			return true
   118  		}
   119  		if _, ok := target.(Code); !ok {
   120  			var te Code
   121  			if ok = As(err, &te); ok && !isNil(reflect.ValueOf(err)) && strings.EqualFold(te.Error(), savedMsg) {
   122  				return true
   123  			}
   124  		}
   125  
   126  		// // TODO: consider supporting target.Is(err). This would allow
   127  		// // user-definable predicates, but also may allow for coping with sloppy
   128  		// // APIs, thereby making it easier to get away with them.
   129  		// if err = Unwrap(err); err == nil {
   130  		// 	errors.Is()
   131  		// 	return false
   132  		// }
   133  
   134  		switch x := err.(type) {
   135  		case interface{ Unwrap() error }:
   136  			err = x.Unwrap() //nolint:revive
   137  			if err == nil {
   138  				return false
   139  			}
   140  		case interface{ Unwrap() []error }:
   141  			for _, err := range x.Unwrap() {
   142  				if Is(err, target) {
   143  					return true
   144  				}
   145  			}
   146  			return false
   147  		default:
   148  			return false
   149  		}
   150  	}
   151  }
   152  
   153  func IsStd(err, target error) bool { //nolint:revive
   154  	if target == nil {
   155  		return err == target
   156  	}
   157  
   158  	isComparable := reflect.TypeOf(target).Comparable()
   159  	for {
   160  		if isComparable && err == target {
   161  			return true
   162  		}
   163  		if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
   164  			return true
   165  		}
   166  		switch x := err.(type) {
   167  		case interface{ Unwrap() error }:
   168  			err = x.Unwrap() //nolint:revive
   169  			if err == nil {
   170  				return false
   171  			}
   172  		case interface{ Unwrap() []error }:
   173  			for _, err := range x.Unwrap() {
   174  				if Is(err, target) {
   175  					return true
   176  				}
   177  			}
   178  			return false
   179  		default:
   180  			return false
   181  		}
   182  	}
   183  }
   184  
   185  func Iss(err error, targets ...error) (matched bool) { //nolint:revive
   186  	if targets == nil {
   187  		return err == nil
   188  	}
   189  	if err == nil {
   190  		return true
   191  	}
   192  
   193  	for _, target := range targets {
   194  		isComparable := reflect.TypeOf(target).Comparable()
   195  		tv := reflect.ValueOf(target)
   196  		// target is not Code-based, try convert source err with target's type, and test whether its plain text message is equal
   197  		var savedMsg string
   198  		if !isNil(tv) {
   199  			savedMsg = target.Error()
   200  		}
   201  		for {
   202  			if isComparable && err == target {
   203  				return true
   204  			}
   205  			if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
   206  				return true
   207  			}
   208  			if _, ok := target.(Code); !ok {
   209  				if ok = As(err, &target); ok && !isNil(reflect.ValueOf(target)) && strings.EqualFold(target.Error(), savedMsg) {
   210  					return true
   211  				}
   212  			}
   213  
   214  			// // TODO: consider supporting target.Is(err). This would allow
   215  			// // user-definable predicates, but also may allow for coping with sloppy
   216  			// // APIs, thereby making it easier to get away with them.
   217  			// if err = Unwrap(err); err == nil {
   218  			// 	return false
   219  			// }
   220  
   221  			switch x := err.(type) {
   222  			case interface{ Unwrap() error }:
   223  				err = x.Unwrap() //nolint:revive
   224  				if err == nil {
   225  					return false
   226  				}
   227  			case interface{ Unwrap() []error }:
   228  				for _, err := range x.Unwrap() {
   229  					if Is(err, target) {
   230  						return true
   231  					}
   232  				}
   233  				return false
   234  			default:
   235  				// return false
   236  				//
   237  				// here is a bug which causes the rest errors expect the first one could never be processed.
   238  				// this bug might cause the unexpected testing result returned.
   239  			}
   240  		}
   241  	}
   242  	return
   243  }
   244  
   245  // isNil for go1.12+, the difference is it never panic on unavailable kinds.
   246  // see also reflect.IsNil.
   247  func isNil(v reflect.Value) bool {
   248  	return isNilv(&v)
   249  }
   250  
   251  // IsNilv for go1.12+, the difference is it never panic on unavailable kinds.
   252  // see also reflect.IsNil.
   253  func isNilv(v *reflect.Value) bool {
   254  	if v != nil {
   255  		switch k := v.Kind(); k { //nolint:exhaustive //no need
   256  		case reflect.Uintptr:
   257  			if v.CanAddr() {
   258  				return v.UnsafeAddr() == 0 // special: reflect.IsNil assumed nil check on an uintptr is illegal, faint!
   259  			}
   260  		case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
   261  			return v.IsNil()
   262  		case reflect.UnsafePointer:
   263  			return v.Pointer() == 0 // for go1.11, this is a workaround even not bad
   264  		case reflect.Interface, reflect.Slice:
   265  			return v.IsNil()
   266  			// case reflect.Array:
   267  			//	// never true, for an array, it is never IsNil
   268  			// case reflect.String:
   269  			// case reflect.Struct:
   270  		}
   271  	}
   272  	return false
   273  }
   274  
   275  // IsSlice tests err.Is for errs slice
   276  func IsSlice(errs []error, target error) bool { //nolint:revive
   277  	if target == nil {
   278  		// for _, e := range errs {
   279  		//	if e == target {
   280  		//		return true
   281  		//	}
   282  		// }
   283  		return false
   284  	}
   285  
   286  	isComparable := reflect.TypeOf(target).Comparable()
   287  	for {
   288  		if isComparable {
   289  			for _, e := range errs {
   290  				if e == target {
   291  					return true
   292  				}
   293  			}
   294  			// return false
   295  		}
   296  
   297  		for _, e := range errs {
   298  			if x, ok := e.(interface{ Is(error) bool }); ok && x.Is(target) {
   299  				return true
   300  			}
   301  			// if err := Unwrap(e); err == nil {
   302  			//	return false
   303  			// }
   304  		}
   305  		return false //nolint:staticcheck
   306  	}
   307  }
   308  
   309  // TypeIs reports whether any error in `err`'s chain matches target.
   310  //
   311  // The chain consists of err itself followed by the sequence of errors obtained by
   312  // repeatedly calling Unwrap.
   313  //
   314  // An error is considered to match a target if it is equal to that target or if
   315  // it implements a method Is(error) bool such that Is(target) returns true.
   316  func TypeIs(err, target error) bool { //nolint:revive
   317  	if target == nil {
   318  		return err == target
   319  	}
   320  
   321  	isComparable := reflect.TypeOf(target).Comparable()
   322  	for {
   323  		if isComparable {
   324  			if reflect.TypeOf(target) == reflect.TypeOf(err) {
   325  				return true
   326  			}
   327  		}
   328  		if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
   329  			return true
   330  		}
   331  
   332  		// // TODO: consider supporting target.Is(err). This would allow
   333  		// // user-definable predicates, but also may allow for coping with sloppy
   334  		// // APIs, thereby making it easier to get away with them.
   335  		// if err = Unwrap(err); err == nil {
   336  		// 	return false
   337  		// }
   338  
   339  		switch x := err.(type) {
   340  		case interface{ Unwrap() error }:
   341  			err = x.Unwrap() //nolint:revive
   342  			if err == nil {
   343  				return false
   344  			}
   345  		case interface{ Unwrap() []error }:
   346  			for _, err := range x.Unwrap() {
   347  				if Is(err, target) {
   348  					return true
   349  				}
   350  			}
   351  			return false
   352  		default:
   353  			return false
   354  		}
   355  	}
   356  }
   357  
   358  // TypeIsSlice tests err.Is for errs slice
   359  func TypeIsSlice(errs []error, target error) bool { //nolint:revive
   360  	if target == nil {
   361  		// for _, e := range errs {
   362  		//	if e == target {
   363  		//		return true
   364  		//	}
   365  		// }
   366  		return false
   367  	}
   368  
   369  	isComparable := reflect.TypeOf(target).Comparable()
   370  	for {
   371  		if isComparable {
   372  			tt := reflect.TypeOf(target)
   373  			for _, e := range errs {
   374  				// if e == target {
   375  				//	return true
   376  				// }
   377  				if reflect.TypeOf(e) == tt {
   378  					return true
   379  				}
   380  			}
   381  			// return false
   382  		}
   383  
   384  		for _, e := range errs {
   385  			if x, ok := e.(interface{ Is(error) bool }); ok && x.Is(target) {
   386  				return true
   387  			}
   388  			// if err := Unwrap(e); err == nil {
   389  			//	return false
   390  			// }
   391  		}
   392  		return false //nolint:staticcheck
   393  	}
   394  }
   395  
   396  // func As(err error, target interface{}) bool
   397  // func Is(err, target error) bool
   398  // func New(text string) error
   399  // func Unwrap(err error) error
   400  
   401  // Unwrap returns the result of calling the Unwrap method on err, if
   402  // `err`'s type contains an Unwrap method returning error.
   403  // Otherwise, Unwrap returns nil.
   404  //
   405  // An errors.Error is an unwrappable error object, all its inner errors
   406  // can be unwrapped in turn. Therefore it maintains an internal unwrapping
   407  // index and it can't be reset externally. The only approach to clear it
   408  // and make Unwrap work from head is, to keep Unwrap till this turn ending
   409  // by returning nil.
   410  //
   411  // Examples for Unwrap:
   412  //
   413  //	var err = errors.New("hello").WithErrors(io.EOF, io.ShortBuffers)
   414  //	var e error = err
   415  //	for e != nil {
   416  //	    e = errors.Unwrap(err)
   417  //	    // test if e is not nil and process it...
   418  //	}
   419  func Unwrap(err error) error {
   420  	u, ok := err.(interface {
   421  		Unwrap() error
   422  	})
   423  	if !ok {
   424  		return nil
   425  	}
   426  	return u.Unwrap()
   427  }
   428  
   429  // Wrap returns an error annotating err with a Stack trace
   430  // at the point Wrap is called, and the supplied message.
   431  // If err is nil, Wrap returns nil.
   432  func Wrap(err error, message string, args ...interface{}) *WithStackInfo { //nolint:revive
   433  	if err == nil {
   434  		return nil
   435  	}
   436  
   437  	if len(args) > 0 {
   438  		message = fmt.Sprintf(message, args...) //nolint:revive
   439  	}
   440  
   441  	return &WithStackInfo{
   442  		causes2: causes2{
   443  			Causers: []error{err},
   444  			msg:     message,
   445  		},
   446  		Stack: callers(1),
   447  	}
   448  }