Skip to content

Commit

Permalink
Add convenience function for repanicing and maintaining the original …
Browse files Browse the repository at this point in the history
…object, even when handling non-error types such as int and string.
  • Loading branch information
warpfork committed Mar 26, 2015
1 parent 918d306 commit f313887
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 1 deletion.
29 changes: 29 additions & 0 deletions try/try.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
30 changes: 29 additions & 1 deletion try/try_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func ExampleStringCrashInFinally() {
// outer error caught
}

func ExampleStringRethrowInFinally() {
func ExampleStringRepanicCoversFinally() {
try.Do(func() {
try.Do(func() {
fmt.Println("function called")
Expand Down Expand Up @@ -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
}

0 comments on commit f313887

Please sign in to comment.