github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/go/d/try.go (about) 1 // Copyright 2016 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 // Package d implements several debug, error and assertion functions used throughout Noms. 6 package d 7 8 import ( 9 "errors" 10 "fmt" 11 "reflect" 12 13 "github.com/stretchr/testify/assert" 14 ) 15 16 // d.Chk.<Method>() -- used in test cases and as assertions 17 var ( 18 Chk = assert.New(&panicker{}) 19 ) 20 21 type panicker struct { 22 } 23 24 func (s panicker) Errorf(format string, args ...interface{}) { 25 panic(fmt.Sprintf(format, args...)) 26 } 27 28 // Panic(err) creates an error using format and args and wraps it in a 29 // WrappedError which can be handled using Try() and TryCatch() 30 func Panic(format string, args ...interface{}) { 31 32 if len(args) == 0 { 33 err := errors.New(format) 34 panic(Wrap(err)) 35 } 36 err := fmt.Errorf(format, args...) 37 panic(Wrap(err)) 38 } 39 40 // PanicIfError(err) && PanicIfTrue(expr) can be used to panic in a way that's 41 // easily handled by Try() and TryCatch() 42 func PanicIfError(err error) { 43 if err != nil { 44 panic(Wrap(err)) 45 } 46 } 47 48 // If b is true, creates a default error, wraps it and panics. 49 func PanicIfTrue(b bool) { 50 if b { 51 panic(Wrap(errors.New("Expected true"))) 52 } 53 } 54 55 // If b is false, creates a default error, wraps it and panics. 56 func PanicIfFalse(b bool) { 57 if !b { 58 panic(Wrap(errors.New("Expected false"))) 59 } 60 } 61 62 // If 'f' panics with a WrappedError then recover that error. 63 // If types is empty, return the WrappedError. 64 // if types is not empty and cause is not one of the listed types, re-panic. 65 // if types is not empty and cause is one of the types, return 'cause' 66 func Try(f func(), types ...interface{}) (err error) { 67 defer recoverWrappedTypes(&err, types) 68 f() 69 return 70 } 71 72 // If 'f' panics with a WrappedError then recover that error and return it. 73 // If types is empty, return the WrappedError. 74 // if types is not empty and cause is not one of the listed types, re-panic. 75 // if types is not empty and cause is one of the types, return 'cause' 76 func TryCatch(f func(), catch func(err error) error) (err error) { 77 defer recoverWrapped(&err, catch) 78 f() 79 return 80 } 81 82 type WrappedError interface { 83 Error() string 84 Cause() error 85 } 86 87 // Wraps an error. The enclosing error has a default Error() that contains the error msg along 88 // with a backtrace. The original error can be retrieved by calling err.Cause(). 89 func Wrap(err error) WrappedError { 90 if err == nil { 91 return nil 92 } 93 if we, ok := err.(WrappedError); ok { 94 return we 95 } 96 97 st := stackTracer{} 98 assert := assert.New(&st) 99 assert.Fail(err.Error()) 100 101 return wrappedError{st.stackTrace, err} 102 } 103 104 // If err is a WrappedError, then Cause() is returned, otherwise returns err. 105 func Unwrap(err error) error { 106 cause := err 107 we, ok := err.(WrappedError) 108 if ok { 109 cause = we.Cause() 110 } 111 return cause 112 } 113 114 func causeInTypes(err error, types ...interface{}) bool { 115 cause := Unwrap(err) 116 typ := reflect.TypeOf(cause) 117 for _, t := range types { 118 if typ == reflect.TypeOf(t) { 119 return true 120 } 121 } 122 return false 123 } 124 125 // Utility method, that checks type of error and panics with wrapped error not one of the listed types. 126 func PanicIfNotType(err error, types ...interface{}) error { 127 if err == nil { 128 return nil 129 } 130 if !causeInTypes(err, types...) { 131 we, ok := err.(WrappedError) 132 if !ok { 133 we = Wrap(err) 134 } 135 panic(we) 136 } 137 return Unwrap(err) 138 } 139 140 type wrappedError struct { 141 msg string 142 cause error 143 } 144 145 func (we wrappedError) Error() string { return we.msg } 146 func (we wrappedError) Cause() error { return we.cause } 147 148 type stackTracer struct { 149 stackTrace string 150 } 151 152 func (s *stackTracer) Errorf(format string, args ...interface{}) { 153 s.stackTrace = fmt.Sprintf(format, args...) 154 } 155 156 func recoverWrappedTypes(errp *error, types []interface{}) { 157 if r := recover(); r != nil { 158 if wrapper, ok := r.(wrappedError); !ok { 159 panic(r) 160 } else if len(types) > 0 && !causeInTypes(wrapper, types...) { 161 panic(r) 162 } else if len(types) > 0 { 163 *errp = wrapper.Cause() 164 } else { 165 *errp = wrapper 166 } 167 } 168 } 169 170 func recoverWrapped(errp *error, catch func(err error) error) { 171 if r := recover(); r != nil { 172 we, ok := r.(wrappedError) 173 if !ok { 174 panic(r) 175 } 176 if catch != nil { 177 *errp = catch(we) 178 } else { 179 *errp = Unwrap(we) 180 } 181 } 182 }