diff --git a/try/try.go b/try/try.go index 5048fe3..414e8df 100644 --- a/try/try.go +++ b/try/try.go @@ -169,3 +169,32 @@ func (p *Plan) Done() { }() p.main() } + +/* + Panics again with the original error. + + If the error is a `UnknownPanicError` (i.e., a `CatchAll` block that's handling something + that wasn't originally an `error` type, so it was wrapped), it will unwrap that re-panic + with that original error -- in other words, this is a "do the right thing" method in all scenarios. + + You may simply `panic(err)` for all other typed `Catch` blocks (since they are never wraps), + though there's no harm in using `Repanic` consistently if you prefer. It is also safe to + use `Repanic` on other non-`error` types (though you're really not helping anyone with that + behavior) and objects that didn't originally come in as the handler's error. No part of + the error handling or finally handlers chain will change execution order as a result; + `panic` and `Repanic` cause identical behavior in that regard. +*/ +func Repanic(err error) { + wrapper, ok := err.(*errors.Error) + if !ok { + panic(err) + } + if !wrapper.Is(UnknownPanicError) { + panic(err) + } + data := errors.GetData(err, OriginalPanic) + if data == nil { + panic(errors.ProgrammerError.New("misuse of try internals", errors.SetData(OriginalPanic, err))) + } + panic(data) +} diff --git a/try/try_test.go b/try/try_test.go index 667ddf5..c6131d8 100644 --- a/try/try_test.go +++ b/try/try_test.go @@ -268,7 +268,7 @@ func ExampleStringCrashInFinally() { // outer error caught } -func ExampleStringRethrowInFinally() { +func ExampleStringRepanicCoversFinally() { try.Do(func() { try.Do(func() { fmt.Println("function called") @@ -298,3 +298,31 @@ func ExampleStringRethrowInFinally() { // finally block called // outer error caught } + +// this is a particularly useful pattern for doing cleanup on error, but not on success. +func ExampleObjectRepanicOriginal() { + obj := struct{}{} + try.Do(func() { + try.Do(func() { + fmt.Println("function called") + panic(obj) + }).Finally(func() { + fmt.Println("finally block called") + }).CatchAll(func(e error) { + fmt.Println("catch wildcard called") + // repanic... with the original error! + try.Repanic(e) + }).Done() + }).CatchAll(func(e error) { + // this example is a little funny, because it got re-wrapped again + // but the important part is yes, the (pointer equal!) object is in there. + data := errors.GetData(e, try.OriginalPanic) + fmt.Printf("outer error equals original: %v\n", data == obj) + }).Done() + + // Output: + // function called + // catch wildcard called + // finally block called + // outer error equals original: true +}