github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/throw/stackwrap.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package throw
     7  
     8  import "reflect"
     9  
    10  // WithStackExt wraps the error with stack of caller. Nil value will return nil.
    11  func WithStack(err error) error {
    12  	return WithStackExt(err, 1)
    13  }
    14  
    15  func WithStackAndDetails(predecessor error, details interface{}) error {
    16  	return WithStackExt(WithDetails(predecessor, details), 1)
    17  }
    18  
    19  // WithStackTop wraps the error with stack's topmost entry only. Nil value will return nil.
    20  // Use this method to augment an error specific to a code location.
    21  func WithStackTop(err error) error {
    22  	return WithStackTopExt(err, 1)
    23  }
    24  
    25  // WithStackExt wraps the error with stack with the given number of frames skipped. Nil value will return nil.
    26  func WithStackExt(err error, skipFrames int) error {
    27  	if err == nil {
    28  		return nil
    29  	}
    30  	if skipFrames < 0 {
    31  		skipFrames = 0
    32  	}
    33  	return withStack(err, CaptureStack(skipFrames+1))
    34  }
    35  
    36  // WithStackTopExt wraps the error with stack's topmost entry after skipping the given number of frames. Nil value will return nil.
    37  func WithStackTopExt(err error, skipFrames int) error {
    38  	if err == nil {
    39  		return nil
    40  	}
    41  	if skipFrames < 0 {
    42  		skipFrames = 0
    43  	}
    44  	return withStack(err, CaptureStackTop(skipFrames+1))
    45  }
    46  
    47  func reuseSupersetTrace(current, wrapped StackTrace) (StackTrace, DeepestStackMode) {
    48  	switch {
    49  	case current == nil:
    50  		return wrapped, InheritedTrace
    51  	case wrapped == nil:
    52  		return nil, 0
    53  	}
    54  	switch CompareStackTraceExt(current, wrapped, SameMethod) {
    55  	case SubsetStack, StackTop:
    56  		return wrapped, InheritedTrace
    57  	case FullStack:
    58  		return nil, SupersededTrace
    59  	default:
    60  		return nil, 0
    61  	}
    62  }
    63  
    64  func withStack(err error, st StackTrace) stackWrap {
    65  	if sth := OutermostStack(err); sth != nil {
    66  		stDeep, _ := sth.DeepestStackTrace()
    67  		stDeepest, stDeepMod := reuseSupersetTrace(st, stDeep)
    68  		return stackWrap{st, stDeepest, stDeepMod, err}
    69  	}
    70  	return stackWrap{st: st, err: err}
    71  }
    72  
    73  func WithDetails(predecessor error, details ...interface{}) error {
    74  	switch {
    75  	case len(details) == 0:
    76  		return predecessor
    77  	case predecessor == nil:
    78  		return WithDetails(NewDescription(details[0]), details[1:]...)
    79  	default:
    80  		for _, d := range details {
    81  			if d == nil {
    82  				continue
    83  			}
    84  			predecessor = withDetails(predecessor, d)
    85  		}
    86  		return predecessor
    87  	}
    88  }
    89  
    90  func withDetails(predecessor error, details interface{}) error {
    91  	var d fmtWrap
    92  	switch vv := details.(type) {
    93  	case fmtWrap:
    94  		d = vv
    95  	case panicWrap:
    96  		d = vv.fmtWrap
    97  	case msgWrap:
    98  		d.msg = vv.msg
    99  	case stackWrap:
   100  		if vv.err == nil {
   101  			return predecessor
   102  		}
   103  		return withDetails(predecessor, vv.err)
   104  	case nil:
   105  		// nil is handled by caller
   106  		panic("illegal value")
   107  	default:
   108  		d = wrapInternal(details)
   109  	}
   110  
   111  	return detailsWrap{err: predecessor, details: d, isComparable: reflect.TypeOf(details).Comparable()}
   112  }