github.com/mitranim/gg@v0.1.17/try.go (about) 1 package gg 2 3 import "os" 4 5 /* 6 If the error is nil, returns void. If the error is non-nil, panics with that 7 error, idempotently adding a stack trace. 8 */ 9 func Try(err error) { 10 if err != nil { 11 panic(ErrTracedAt(err, 1)) 12 } 13 } 14 15 /* 16 If the error is nil, returns void. If the error is non-nil, panics with that 17 error, idempotently adding a stack trace and skipping the given number of stack 18 frames. 19 */ 20 func TryAt(err error, skip int) { 21 if err != nil { 22 panic(ErrTracedAt(err, skip+1)) 23 } 24 } 25 26 /* 27 If the error is nil, returns void. If the error is non-nil, panics with that 28 exact error. Unlike `Try` and other similar functions, this function does NOT 29 generate a stack trace or modify the error in any way. Most of the time, you 30 should prefer `Try` and other similar functions. This function is intended 31 for "internal" library code, for example to avoid the stack trace check when 32 the trace is guaranteed, or to avoid generating the trace when the error is 33 meant to be caught before being returned to the caller. 34 */ 35 func TryErr(err error) { 36 if err != nil { 37 panic(err) 38 } 39 } 40 41 /* 42 Like `Try`, but for multiple errors. Uses `Errs.Err` to combine the errors. 43 If the resulting error is nil, returns void. If the resulting error is non-nil, 44 panics with that error, idempotently adding a stack trace. 45 */ 46 func TryMul(src ...error) { 47 err := ErrMul(src...) 48 if err != nil { 49 panic(ErrTracedAt(err, 1)) 50 } 51 } 52 53 /* 54 If the error is nil, returns the given value. If the error is non-nil, panics 55 with that error, idempotently adding a stack trace. 56 */ 57 func Try1[A any](val A, err error) A { 58 if err != nil { 59 panic(ErrTracedAt(err, 1)) 60 } 61 return val 62 } 63 64 /* 65 If the error is nil, returns the given values. If the error is non-nil, panics 66 with that error, idempotently adding a stack trace. 67 */ 68 func Try2[A, B any](one A, two B, err error) (A, B) { 69 if err != nil { 70 panic(ErrTracedAt(err, 1)) 71 } 72 return one, two 73 } 74 75 /* 76 If the error is nil, returns the given values. If the error is non-nil, panics 77 with that error, idempotently adding a stack trace. 78 */ 79 func Try3[A, B, C any](one A, two B, three C, err error) (A, B, C) { 80 if err != nil { 81 panic(ErrTracedAt(err, 1)) 82 } 83 return one, two, three 84 } 85 86 /* 87 Shortcut for use with `recover()`. Useful for writing deferrable functions that 88 deal with panics. If the given recovered value is nil, this does nothing. 89 Otherwise converts it to an error, idempotently generating a stack trace, and 90 panics with the resulting traced error. Usage: 91 92 gg.TryAny(recover()) 93 */ 94 func TryAny(val any) { 95 if val != nil { 96 panic(AnyErrTracedAt(val, 1)) 97 } 98 } 99 100 /* 101 Shortcut for use with `recover()`. Useful for writing deferrable functions that 102 deal with panics. If the given recovered value is nil, this does nothing. 103 Otherwise converts it to an error, idempotently generating a stack trace, 104 skipping the given number of frames, and panics with the resulting traced 105 error. Usage: 106 107 gg.TryAnyAt(recover(), 1) 108 */ 109 func TryAnyAt(val any, skip int) { 110 if val != nil { 111 panic(AnyErrTracedAt(val, skip+1)) 112 } 113 } 114 115 /* 116 If the given error is nil, does nothing. Otherwise wraps the error with `Wrap` 117 and the given message, and panics with the resulting error. 118 */ 119 func TryWrap(err error, msg ...any) { 120 if err != nil { 121 panic(Wrap(err, msg...)) 122 } 123 } 124 125 /* 126 If the given error is nil, does nothing. Otherwise wraps the error with `Wrapf` 127 and the given message, and panics with the resulting error. 128 */ 129 func TryWrapf(err error, pat string, arg ...any) { 130 if err != nil { 131 panic(Wrapf(err, pat, arg...)) 132 } 133 } 134 135 /* 136 Must be deferred. Recovers from panics, writing the resulting error, if any, to 137 the given pointer. Should be used together with "try"-style functions. 138 Idempotently adds a stack trace. 139 */ 140 func Rec(out *error) { 141 if out == nil { 142 return 143 } 144 145 err := AnyErrTracedAt(recover(), 1) 146 if err != nil { 147 *out = err 148 } 149 } 150 151 /* 152 Must be deferred. Recovers from panics, writing the resulting error, if any, to 153 the given pointer. Should be used together with "try"-style functions. Unlike 154 `Rec` and other similar functions, this function does NOT generate a stack 155 trace or modify the error in any way. Most of the time, you should prefer `Rec` 156 and other similar functions. This function is intended for "internal" library 157 code, for example to avoid the stack trace check when the trace is guaranteed, 158 or to avoid generating the trace when the error is meant to be caught before 159 being returned to the caller. 160 */ 161 func RecErr(out *error) { 162 if out == nil { 163 return 164 } 165 166 err := AnyErr(recover()) 167 if err != nil { 168 *out = err 169 } 170 } 171 172 /* 173 Must be deferred. Same as `Rec`, but skips the given amount of stack frames when 174 capturing a trace. 175 */ 176 func RecN(out *error, skip int) { 177 if out == nil { 178 return 179 } 180 181 err := AnyErrTracedAt(recover(), skip+1) 182 if err != nil { 183 *out = err 184 } 185 } 186 187 /* 188 Must be deferred. Filtered version of `Rec`. Recovers from panics that 189 satisfy the provided test. Re-panics on non-nil errors that don't satisfy the 190 test. Does NOT check errors that are returned normally, without a panic. 191 Idempotently adds a stack trace. 192 */ 193 func RecOnly(ptr *error, test func(error) bool) { 194 err := AnyErrTracedAt(recover(), 1) 195 if err == nil { 196 return 197 } 198 199 *ptr = err 200 if test != nil && test(err) { 201 return 202 } 203 204 panic(err) 205 } 206 207 /* 208 Must be deferred. Recovery for background goroutines which are not allowed to 209 crash. Calls the provided function ONLY if the error is non-nil. 210 */ 211 func RecWith(fun func(error)) { 212 err := AnyErrTracedAt(recover(), 1) 213 if err != nil && fun != nil { 214 fun(err) 215 } 216 } 217 218 /* 219 Runs the given function, converting a panic to an error. 220 Idempotently adds a stack trace. 221 */ 222 func Catch(fun func()) (err error) { 223 defer RecN(&err, 1) 224 if fun != nil { 225 fun() 226 } 227 return 228 } 229 230 /* 231 Runs the given function with the given input, converting a panic to an error. 232 Idempotently adds a stack trace. Compare `Catch01` and `Catch11`. 233 */ 234 func Catch10[A any](fun func(A), val A) (err error) { 235 defer RecN(&err, 1) 236 if fun != nil { 237 fun(val) 238 } 239 return 240 } 241 242 /* 243 Runs the given function, returning the function's result along with its panic 244 converted to an error. Idempotently adds a stack trace. Compare `Catch10` and 245 `Catch11`. 246 */ 247 func Catch01[A any](fun func() A) (val A, err error) { 248 defer RecN(&err, 1) 249 if fun != nil { 250 val = fun() 251 } 252 return 253 } 254 255 /* 256 Runs the given function with the given input, returning the function's result 257 along with its panic converted to an error. Idempotently adds a stack trace. 258 Compare `Catch10` and `Catch01`. 259 */ 260 func Catch11[A, B any](fun func(A) B, val0 A) (val1 B, err error) { 261 defer RecN(&err, 1) 262 if fun != nil { 263 val1 = fun(val0) 264 } 265 return 266 } 267 268 /* 269 Runs a given function, converting a panic to an error IF the error satisfies 270 the provided test. Idempotently adds a stack trace. 271 */ 272 func CatchOnly(test func(error) bool, fun func()) (err error) { 273 defer RecOnly(&err, test) 274 if fun != nil { 275 fun() 276 } 277 return 278 } 279 280 /* 281 Shortcut for `Catch() != nil`. Useful when you want to handle all errors while 282 ignoring their content. 283 */ 284 func Caught(fun func()) bool { 285 return Catch(fun) != nil 286 } 287 288 /* 289 Shortcut for `CatchOnly() != nil`. Useful when you want to handle a specific 290 error while ignoring its content. 291 */ 292 func CaughtOnly(test func(error) bool, fun func()) bool { 293 return CatchOnly(test, fun) != nil 294 } 295 296 /* 297 Must be deferred. Catches panics; ignores errors that satisfy the provided 298 test; re-panics on other non-nil errors. Idempotently adds a stack trace. 299 */ 300 func SkipOnly(test func(error) bool) { 301 err := AnyErrTracedAt(recover(), 1) 302 if err != nil && test != nil && test(err) { 303 return 304 } 305 Try(err) 306 } 307 308 // Runs a function, catching and ignoring ALL panics. 309 func Skipping(fun func()) { 310 defer Skip() 311 if fun != nil { 312 fun() 313 } 314 } 315 316 /* 317 Runs a function, catching and ignoring only the panics that satisfy the provided 318 test. Idempotently adds a stack trace. 319 */ 320 func SkippingOnly(test func(error) bool, fun func()) { 321 defer SkipOnly(test) 322 if fun != nil { 323 fun() 324 } 325 } 326 327 // Must be deferred. Catches and ignores ALL panics. 328 func Skip() { _ = recover() } 329 330 /* 331 Must be deferred. Tool for adding a stack trace to an arbitrary panic. Unlike 332 the "rec" functions, this does NOT prevent the panic from propagating. It 333 simply ensures that there's a stack trace, then re-panics. 334 335 Caution: due to idiosyncrasies of `recover()`, this works ONLY when deferred 336 directly. Anything other than `defer gg.Traced()` will NOT work. 337 */ 338 func Traced() { TryErr(AnyErrTracedAt(recover(), 1)) } 339 340 /* 341 Must be deferred. Version of `Traced` that skips the given number of stack 342 frames when generating a stack trace. 343 */ 344 func TracedAt(skip int) { Try(AnyErrTracedAt(recover(), skip+1)) } 345 346 /* 347 Must be deferred. Runs the function only if there's no panic. Idempotently adds 348 a stack trace. 349 */ 350 func Ok(fun func()) { 351 Try(AnyErrTracedAt(recover(), 1)) 352 if fun != nil { 353 fun() 354 } 355 } 356 357 /* 358 Must be deferred. Runs the function ONLY if there's an ongoing panic, and then 359 re-panics. Idempotently adds a stack trace. 360 */ 361 func Fail(fun func(error)) { 362 err := AnyErrTracedAt(recover(), 1) 363 if err != nil && fun != nil { 364 fun(err) 365 } 366 Try(err) 367 } 368 369 /* 370 Must be deferred. Always runs the given function, passing either the current 371 panic or nil. If the error is non-nil, re-panics. 372 */ 373 func Finally(fun func(error)) { 374 err := AnyErrTracedAt(recover(), 1) 375 if fun != nil { 376 fun(err) 377 } 378 Try(err) 379 } 380 381 /* 382 Must be deferred. Short for "transmute" or "transform". Catches an ongoing 383 panic, transforms the error by calling the provided function, and then 384 re-panics via `Try`. Idempotently adds a stack trace. 385 */ 386 func Trans(fun func(error) error) { 387 err := AnyErrTracedAt(recover(), 1) 388 if err != nil && fun != nil { 389 err = fun(err) 390 } 391 Try(err) 392 } 393 394 /* 395 Runs a function, "transmuting" or "transforming" the resulting panic by calling 396 the provided transformer. See `Trans`. 397 */ 398 func Transing(trans func(error) error, fun func()) { 399 defer Trans(trans) 400 if fun != nil { 401 fun() 402 } 403 } 404 405 /* 406 Must be deferred. Similar to `Trans`, but transforms only non-nil errors that 407 satisfy the given predicate. Idempotently adds a stack trace. 408 */ 409 func TransOnly(test func(error) bool, trans func(error) error) { 410 err := AnyErrTracedAt(recover(), 1) 411 if err != nil && test != nil && trans != nil && test(err) { 412 err = trans(err) 413 } 414 Try(err) 415 } 416 417 /* 418 Must be deferred. Wraps non-nil panics, prepending the error message and 419 idempotently adding a stack trace. The message is converted to a string via 420 `Str(msg...)`. Usage: 421 422 defer gg.Detail(`unable to do X`) 423 defer gg.Detail(`unable to do A with B `, someEntity.Id) 424 */ 425 func Detail(msg ...any) { 426 Try(Wrap(AnyErr(recover()), msg...)) 427 } 428 429 /* 430 Must be deferred. Wraps non-nil panics, prepending the error message and 431 idempotently adding a stack trace. Usage: 432 433 defer gg.Detailf(`unable to %v`, `do X`) 434 435 The first argument must be a hardcoded pattern string compatible with 436 `fmt.Sprintf` and other similar functions. If the first argument is an 437 expression rather than a hardcoded string, use `Detail` instead. 438 */ 439 func Detailf(pat string, arg ...any) { 440 Try(Wrapf(AnyErr(recover()), pat, arg...)) 441 } 442 443 /* 444 Must be deferred. Wraps non-nil panics, prepending the error message, ONLY if 445 they satisfy the provided test. Idempotently adds a stack trace. 446 */ 447 func DetailOnlyf(test func(error) bool, pat string, arg ...any) { 448 err := AnyErrTracedAt(recover(), 1) 449 if err != nil && test != nil && test(err) { 450 err = Wrapf(err, pat, arg...) 451 } 452 Try(err) 453 } 454 455 /* 456 Must be deferred in your `main` function. If there is a panic, this prints a 457 stack trace and kills the process with exit code 1. This is very similar to the 458 default Go behavior, the only difference being how the resulting panic trace is 459 formatted. This prevents Go from printing the default, very informative but 460 difficult to read panic trace, replacing it with a less informative but much 461 easier to read trace. See our types `Err` and `Trace`. Usage example: 462 463 func main() { 464 defer gg.Fatal() 465 // Perform actions that may panic. 466 } 467 */ 468 func Fatal() { 469 val := recover() 470 if val != nil { 471 Nop2(os.Stderr.Write(AnyToErrTracedAt(val, 1).AppendStack(nil))) 472 os.Exit(1) 473 } 474 }