From 67deb24dd496b420d5b5859a385236ab0d274cea Mon Sep 17 00:00:00 2001 From: Matthias Fax Date: Fri, 27 Sep 2024 09:45:13 +0000 Subject: [PATCH 1/4] feat: add Recover functions for panic redirects The Recover functions are more in line with functional programming idioms and golang's error return signature than the existing Try/Catch helpers. --- README.md | 90 ++++++++ recover.go | 599 ++++++++++++++++++++++++++++++++++++++++++++++++ recover_test.go | 386 +++++++++++++++++++++++++++++++ 3 files changed, 1075 insertions(+) create mode 100644 recover.go create mode 100644 recover_test.go diff --git a/README.md b/README.md index c36e9072..7bc572b1 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,10 @@ Error handling: - [TryCatch](#trycatch) - [TryWithErrorValue](#trywitherrorvalue) - [TryCatchWithErrorValue](#trycatchwitherrorvalue) +- [Recover0 -> Recover6](#recover0-6) +- [Recover0Error -> Recover6Error](#recover0-6error) +- [Recover0Typed -> Recover6Typed](#recover0-6typed) +- [Recover0ErrorTyped -> Recover6ErrorTyped](#recover0-6errortyped) - [ErrorsAs](#errorsas) Constraints: @@ -3465,6 +3469,92 @@ ok := lo.TryCatchWithErrorValue(func() error { [[play](https://go.dev/play/p/8Pc9gwX_GZO)] +### Recover{0->6} + +The `Recover{0->6}` functions are designed to encapsulate panicking functions, redirecting panic `error` interfaces and `string` types to golang's idiomatic return signature. This is helpful with library functions where an error types has to be returned in addition to the existing return types. + +```go +// Example of a library function +func errorOrNil[T any](callback func(string) T) (T, error) { + return lo.Recover1(callback("test")) +} + +errorOrNil(func(param string) string { + panic("panicking") +}) +// Output: "", "panicking" + +errorOrNil(func(param string) string { + return param +}) +// Output: "test", nil +``` + +### Recover{0->6}Error + +These functions merge both `Recover{0->6}` and callback error return types into a unified error type. This can be helpful in situations where non-error inline functions are passed to library functions that come with their own error return, making panic the only workaround to pass errors from the callback function to the encapsulating code. + +```go +// This could be a library function +// Example of a library function +func externalLibraryFunction(callback func(string) string) (string, error) { + _ = callback("test") + return "", errors.New("final error") +} + +lo.Recover1Error(externalLibraryFunction(func(param string) string { + panic("panicking") +})) +// Output: "", "panicking" + +lo.Recover1Error(externalLibraryFunction(func(param string) string { + return param +})) +// Output: "", "final error" +``` + +### Recover{0->6}Typed + +The `Recover{0->6}Typed` functions extend the `Recover{0->6}` with an additional type parameter to define the panic error type to be cought, and an optional (variadic) flag to define whether to catch strings as well. + +```go +// Example of a library function +func errorOrNil[T any](callback func(string) T) (T, error) { + return lo.Recover1Typed[myError](callback("test"), false) +} + +errorOrNil(func(param string) string { + panic("no recovery") +}) +// Panic: "no recovery" + +errorOrNil(func(param string) string { + panic(erros.New("no recovery")) +}) +// Panic: "no recovery" +``` + +### Recover{0->6}ErrorTyped + +The `Recover{0->6}ErrorTyped` functions combine the callback error redirect from `Recover{0->6}Error` and the catch type specification from `Recover{0->6}Typed`. + +```go +// Example of a library function +func errorOrNil[T any](callback func() T) (T, error) { + return lo.Recover0ErrorTyped[myError](callback(), true) +} + +errorOrNil(func() string { + panic("recovery") +}) +// Output: "", "recovery" + +errorOrNil(func() string { + return "test" +}) +// Output: "test", nil +``` + ### ErrorsAs A shortcut for: diff --git a/recover.go b/recover.go new file mode 100644 index 00000000..ba517667 --- /dev/null +++ b/recover.go @@ -0,0 +1,599 @@ +package lo + +import "errors" + +// Recover0 executes a callback function and recovers from any panic that occurs, +// converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It does not return any values. +// +// Returns: +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover0(callback func()) (err error) { + return Recover0Typed[error](callback, true) +} + +// Recover0Error executes a callback function that returns an error, and recovers +// from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns an error. +// +// Returns: +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover0Error(callback func() error) (err error) { + return Recover0ErrorTyped[error](callback, true) +} + +// Recover1 executes a callback function that returns a single value and recovers from any panic that occurs, +// converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns a single value of type A. +// +// Returns: +// - r1: The value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover1[A any](callback func() A) (r1 A, err error) { + return Recover1Typed[A, error](callback, true) +} + +// Recover1Error executes a callback function that returns a single value and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns a single value of type A and an error. +// +// Returns: +// - r1: The value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover1Error[A any](callback func() (A, error)) (r1 A, err error) { + return Recover1ErrorTyped[A, error](callback, true) +} + +// Recover2 executes a callback function that returns two values and recovers from +// any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns two values of types A and B. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover2[A, B any](callback func() (A, B)) (r1 A, r2 B, err error) { + return Recover2Typed[A, B, error](callback, true) +} + +// Recover2Error executes a callback function that returns two values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns two values of types A and B, and an error. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover2Error[A, B any](callback func() (A, B, error)) (r1 A, r2 B, err error) { + return Recover2ErrorTyped[A, B, error](callback, true) +} + +// Recover3 executes a callback function that returns three values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns three values of types A, B, and C. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover3[A, B, C any](callback func() (A, B, C)) (r1 A, r2 B, r3 C, err error) { + return Recover3Typed[A, B, C, error](callback, true) +} + +// Recover3Error executes a callback function that returns three values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns three values of types A, B, and C, and an error. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover3Error[A, B, C any](callback func() (A, B, C, error)) (r1 A, r2 B, r3 C, err error) { + return Recover3ErrorTyped[A, B, C, error](callback, true) +} + +// Recover4 executes a callback function that returns four values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns four values of types A, B, C, and D. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover4[A, B, C, D any](callback func() (A, B, C, D)) (r1 A, r2 B, r3 C, r4 D, err error) { + return Recover4Typed[A, B, C, D, error](callback, true) +} + +// Recover4Error executes a callback function that returns four values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns four values of types A, B, C, and D, and an error. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover4Error[A, B, C, D any](callback func() (A, B, C, D, error)) (r1 A, r2 B, r3 C, r4 D, err error) { + return Recover4ErrorTyped[A, B, C, D, error](callback, true) +} + +// Recover5 executes a callback function that returns five values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns five values of types A, B, C, D, and E. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - r5: The fifth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover5[A, B, C, D, E any](callback func() (A, B, C, D, E)) (r1 A, r2 B, r3 C, r4 D, r5 E, err error) { + return Recover5Typed[A, B, C, D, E, error](callback, true) +} + +// Recover5Error executes a callback function that returns five values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns five values of types A, B, C, D, and E, and an error. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - r5: The fifth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover5Error[A, B, C, D, E any](callback func() (A, B, C, D, E, error)) (r1 A, r2 B, r3 C, r4 D, r5 E, err error) { + return Recover5ErrorTyped[A, B, C, D, E, error](callback, true) +} + +// Recover6 executes a callback function that returns six values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns six values of types A, B, C, D, E, and F. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - r5: The fifth value returned by the callback function. +// - r6: The sixth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover6[A, B, C, D, E, F any](callback func() (A, B, C, D, E, F)) (r1 A, r2 B, r3 C, r4 D, r5 E, r6 F, err error) { + return Recover6Typed[A, B, C, D, E, F, error](callback, true) +} + +// Recover6Error executes a callback function that returns six values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns six values of types A, B, C, D, E, F, and an error. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - r5: The fifth value returned by the callback function. +// - r6: The sixth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover6Error[A, B, C, D, E, F any](callback func() (A, B, C, D, E, F, error)) (r1 A, r2 B, r3 C, r4 D, r5 E, r6 F, err error) { + return Recover6ErrorTyped[A, B, C, D, E, F, error](callback, true) +} + +// TypedRecover is a generic function that recovers from a panic and assigns the recovered +// error to the provided error pointer. It can optionally catch string panics and convert +// them to errors. +// +// Parameters: +// - err: A pointer to an error where the recovered error will be stored. +// - catchString: A variadic boolean parameter. If provided and true, string panics will +// be caught and converted to errors. +// +// The function does not return any values. Instead, it modifies the error pointer passed +// as an argument to store the recovered error. +// +// It is intended to be used as deferred function in a function that needs to recover from +// panics. +// +// Example: +// +// func myFunction() { +// defer TypedRecover0[error](func() { fmt.Println("Recovered from panic") }) +// panic("This is a panic") +// } +func TypedRecover[R error](err *error, catchString ...bool) { + catchStr := len(catchString) > 0 && catchString[0] + if r := recover(); r != nil { + if e, ok := r.(R); ok { + *err = e + } else if e, ok := r.(string); catchStr && ok { + *err = errors.New(e) + } else { + panic(r) + } + } +} + +// Recover0Typed executes a callback function and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It does not return any value. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover0Typed[R error](callback func(), catchString ...bool) (err error) { + defer TypedRecover[R](&err, catchString...) + callback() + return +} + +// Recover0ErrorTyped executes a callback function that returns an error and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns an error. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover0ErrorTyped[R error](callback func() error, catchString ...bool) (err error) { + defer TypedRecover[R](&err, catchString...) + err = callback() + return +} + +// Recover1Typed executes a callback function that returns a single value and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns a single value of type A. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover1Typed[A any, R error](callback func() A, catchString ...bool) (r1 A, err error) { + defer TypedRecover[R](&err, catchString...) + r1 = callback() + return +} + +// Recover1ErrorTyped executes a callback function that returns a single value and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns a single value of type A and an error. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover1ErrorTyped[A any, R error](callback func() (A, error), catchString ...bool) (r1 A, err error) { + defer TypedRecover[R](&err, catchString...) + r1, err = callback() + return +} + +// Recover2Typed executes a callback function that returns two values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns two values of types A and B. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover2Typed[A, B any, R error](callback func() (A, B), catchString ...bool) (r1 A, r2 B, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2 = callback() + return +} + +// Recover2ErrorTyped executes a callback function that returns two values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns two values of types A and B, and an error. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover2ErrorTyped[A, B any, R error](callback func() (A, B, error), catchString ...bool) (r1 A, r2 B, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, err = callback() + return +} + +// Recover3Typed executes a callback function that returns three values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns three values of types A, B, and C. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - C: The type of the third value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover3Typed[A, B, C any, R error](callback func() (A, B, C), catchString ...bool) (r1 A, r2 B, r3 C, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, r3 = callback() + return +} + +// Recover3ErrorTyped executes a callback function that returns three values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns three values of types A, B, and C, and an error. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - C: The type of the third value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover3ErrorTyped[A, B, C any, R error](callback func() (A, B, C, error), catchString ...bool) (r1 A, r2 B, r3 C, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, r3, err = callback() + return +} + +// Recover4Typed executes a callback function that returns four values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns four values of types A, B, C, and D. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - C: The type of the third value returned by the callback function. +// - D: The type of the fourth value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover4Typed[A, B, C, D any, R error](callback func() (A, B, C, D), catchString ...bool) (r1 A, r2 B, r3 C, r4 D, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, r3, r4 = callback() + return +} + +// Recover4ErrorTyped executes a callback function that returns four values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns four values of types A, B, C, and D, and an error. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - C: The type of the third value returned by the callback function. +// - D: The type of the fourth value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover4ErrorTyped[A, B, C, D any, R error](callback func() (A, B, C, D, error), catchString ...bool) (r1 A, r2 B, r3 C, r4 D, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, r3, r4, err = callback() + return +} + +// Recover5Typed executes a callback function that returns five values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns five values of types A, B, C, D, and E. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - C: The type of the third value returned by the callback function. +// - D: The type of the fourth value returned by the callback function. +// - E: The type of the fifth value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - r5: The fifth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover5Typed[A, B, C, D, E any, R error](callback func() (A, B, C, D, E), catchString ...bool) (r1 A, r2 B, r3 C, r4 D, r5 E, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, r3, r4, r5 = callback() + return +} + +// Recover5ErrorTyped executes a callback function that returns five values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns five values of types A, B, C, D, and E, and an error. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - C: The type of the third value returned by the callback function. +// - D: The type of the fourth value returned by the callback function. +// - E: The type of the fifth value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - r5: The fifth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover5ErrorTyped[A, B, C, D, E any, R error](callback func() (A, B, C, D, E, error), catchString ...bool) (r1 A, r2 B, r3 C, r4 D, r5 E, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, r3, r4, r5, err = callback() + return +} + +// Recover6Typed executes a callback function that returns six values and recovers from any panic that occurs, +// converting it to an error if possible. It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns six values of types A, B, C, D, E, and F. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - C: The type of the third value returned by the callback function. +// - D: The type of the fourth value returned by the callback function. +// - E: The type of the fifth value returned by the callback function. +// - F: The type of the sixth value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - r5: The fifth value returned by the callback function. +// - r6: The sixth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise nil. +func Recover6Typed[A, B, C, D, E, F any, R error](callback func() (A, B, C, D, E, F), catchString ...bool) (r1 A, r2 B, r3 C, r4 D, r5 E, r6 F, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, r3, r4, r5, r6 = callback() + return +} + +// Recover6ErrorTyped executes a callback function that returns six values and an error, +// and recovers from any panic that occurs, converting it to an error if possible. +// It specifically recovers from panics of type error or string. +// +// Parameters: +// - callback: A function to be executed. It returns six values of types A, B, C, D, E, F, and an error. +// - catchString: An optional boolean flag indicating whether to catch string panics and convert them to errors. +// +// Type Parameters: +// - A: The type of the first value returned by the callback function. +// - B: The type of the second value returned by the callback function. +// - C: The type of the third value returned by the callback function. +// - D: The type of the fourth value returned by the callback function. +// - E: The type of the fifth value returned by the callback function. +// - F: The type of the sixth value returned by the callback function. +// - R: The type of error to recover from. It should be an error type. +// +// Returns: +// - r1: The first value returned by the callback function. +// - r2: The second value returned by the callback function. +// - r3: The third value returned by the callback function. +// - r4: The fourth value returned by the callback function. +// - r5: The fifth value returned by the callback function. +// - r6: The sixth value returned by the callback function. +// - err: An error if a panic of type error or string occurred and was recovered, otherwise the error returned by the callback. +func Recover6ErrorTyped[A, B, C, D, E, F any, R error](callback func() (A, B, C, D, E, F, error), catchString ...bool) (r1 A, r2 B, r3 C, r4 D, r5 E, r6 F, err error) { + defer TypedRecover[R](&err, catchString...) + r1, r2, r3, r4, r5, r6, err = callback() + return +} diff --git a/recover_test.go b/recover_test.go new file mode 100644 index 00000000..95abb690 --- /dev/null +++ b/recover_test.go @@ -0,0 +1,386 @@ +package lo + +import ( + "errors" + "fmt" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +type panicError struct{} + +func (e panicError) Error() string { + return "panic test error" +} + +type recoverFunc interface{} +type recoverFuncName string +type testFuncName string + +var ( + panicErr = panicError{} + callbackErr = errors.New("callback function test error") + noErr = errors.New("no error") +) + +type testParams struct { + x uint8 + typed bool + withError bool +} + +var functionMapping = map[recoverFuncName]struct { + params testParams + recoverFunc recoverFunc +}{ + "Recover0": {params: testParams{0, false, false}, recoverFunc: Recover0}, + "Recover1": {params: testParams{1, false, false}, recoverFunc: Recover1[uint8]}, + "Recover2": {params: testParams{2, false, false}, recoverFunc: Recover2[uint8, uint8]}, + "Recover3": {params: testParams{3, false, false}, recoverFunc: Recover3[uint8, uint8, uint8]}, + "Recover4": {params: testParams{4, false, false}, recoverFunc: Recover4[uint8, uint8, uint8, uint8]}, + "Recover5": {params: testParams{5, false, false}, recoverFunc: Recover5[uint8, uint8, uint8, uint8, uint8]}, + "Recover6": {params: testParams{6, false, false}, recoverFunc: Recover6[uint8, uint8, uint8, uint8, uint8, uint8]}, + "Recover0Error": {params: testParams{0, false, true}, recoverFunc: Recover0Error}, + "Recover1Error": {params: testParams{1, false, true}, recoverFunc: Recover1Error[uint8]}, + "Recover2Error": {params: testParams{2, false, true}, recoverFunc: Recover2Error[uint8, uint8]}, + "Recover3Error": {params: testParams{3, false, true}, recoverFunc: Recover3Error[uint8, uint8, uint8]}, + "Recover4Error": {params: testParams{4, false, true}, recoverFunc: Recover4Error[uint8, uint8, uint8, uint8]}, + "Recover5Error": {params: testParams{5, false, true}, recoverFunc: Recover5Error[uint8, uint8, uint8, uint8, uint8]}, + "Recover6Error": {params: testParams{6, false, true}, recoverFunc: Recover6Error[uint8, uint8, uint8, uint8, uint8, uint8]}, + "Recover0Typed": {params: testParams{0, true, false}, recoverFunc: Recover0Typed[error]}, + "Recover1Typed": {params: testParams{1, true, false}, recoverFunc: Recover1Typed[uint8, error]}, + "Recover2Typed": {params: testParams{2, true, false}, recoverFunc: Recover2Typed[uint8, uint8, error]}, + "Recover3Typed": {params: testParams{3, true, false}, recoverFunc: Recover3Typed[uint8, uint8, uint8, error]}, + "Recover4Typed": {params: testParams{4, true, false}, recoverFunc: Recover4Typed[uint8, uint8, uint8, uint8, error]}, + "Recover5Typed": {params: testParams{5, true, false}, recoverFunc: Recover5Typed[uint8, uint8, uint8, uint8, uint8, error]}, + "Recover6Typed": {params: testParams{6, true, false}, recoverFunc: Recover6Typed[uint8, uint8, uint8, uint8, uint8, uint8, error]}, + "Recover0ErrorTyped": {params: testParams{0, true, true}, recoverFunc: Recover0ErrorTyped[error]}, + "Recover1ErrorTyped": {params: testParams{1, true, true}, recoverFunc: Recover1ErrorTyped[uint8, error]}, + "Recover2ErrorTyped": {params: testParams{2, true, true}, recoverFunc: Recover2ErrorTyped[uint8, uint8, error]}, + "Recover3ErrorTyped": {params: testParams{3, true, true}, recoverFunc: Recover3ErrorTyped[uint8, uint8, uint8, error]}, + "Recover4ErrorTyped": {params: testParams{4, true, true}, recoverFunc: Recover4ErrorTyped[uint8, uint8, uint8, uint8, error]}, + "Recover5ErrorTyped": {params: testParams{5, true, true}, recoverFunc: Recover5ErrorTyped[uint8, uint8, uint8, uint8, uint8, error]}, + "Recover6ErrorTyped": {params: testParams{6, true, true}, recoverFunc: Recover6ErrorTyped[uint8, uint8, uint8, uint8, uint8, uint8, error]}, +} + +var testFuncMapping = map[testFuncName]func(t *testing.T, x uint8, inputErr error, rf recoverFunc, typed bool){ + "Success": testSuccess, + "Panic": testPanic, + "Error": testError, + "Unconfined": testUnconfined, + "StringPanic": testStringPanic, + "StringUnconfined": testStringUnconfined, +} + +func TestTypedRecover(t *testing.T) { + t.Parallel() + is := assert.New(t) + + // Test case where no panic occurs but with a different type + var err error + is.PanicsWithError("test error", func() { + defer TypedRecover[myError](&err) + panic(errors.New("test error")) + }, "expected unconfined panic but got nothing") + is.Nil(err, "expected no caught error") + + // Test case where a string panic occurs and catchString is false + is.PanicsWithValue("test string panic", func() { + defer TypedRecover[error](&err, false) + panic("test string panic") + }) + is.Nil(err, "expected no redirected panic string") + + // Test case where no panic occurs + is.NotPanics(func() { + defer TypedRecover[error](&err) + }) + is.NoError(err) + + // Test case where an error panic occurs + is.NotPanics(func() { + defer TypedRecover[error](&err) + panic(errors.New("test error")) + }) + is.EqualError(err, "test error") + + // Test case where a custom error panic occurs + is.NotPanics(func() { + defer TypedRecover[myError](&err) + panic(myError{}) + }) + is.EqualError(err, myError{}.Error()) + + // Test case where a string panic occurs and catchString is true + is.NotPanics(func() { + defer TypedRecover[error](&err, true) + panic("test string panic") + }) + is.EqualError(err, "test string panic") +} + +func callRecover(rf recoverFunc, x uint8, f func(), inputErr error, catchString bool, typed bool) ([]uint8, error) { + if x > 6 { + panic("Unsupported X value") + } + + results := make([]uint8, x) + var err error + + nilErrorType := reflect.TypeOf((*error)(nil)).Elem() + + callbackOutTypes := make([]reflect.Type, x) + for i := range callbackOutTypes { + callbackOutTypes[i] = reflect.TypeOf(uint8(0)) + } + if inputErr != nil { + callbackOutTypes = append(callbackOutTypes, nilErrorType) + } + inTypes := make([]reflect.Type, 1, 2) + inTypes[0] = reflect.FuncOf(nil, callbackOutTypes, false) + + if typed { + inTypes = append(inTypes, reflect.SliceOf(reflect.TypeOf(bool(false)))) + } + + outTypes := make([]reflect.Type, x+1) + for i := uint8(0); i < x; i++ { + outTypes[i] = reflect.TypeOf(uint8(0)) + } + outTypes[x] = nilErrorType + + dynamicFuncType := reflect.FuncOf(inTypes, outTypes, typed) + + callArgs := make([]reflect.Value, 1, 2) + callArgs[0] = reflect.MakeFunc(inTypes[0], func([]reflect.Value) []reflect.Value { + f() + returnValues := make([]reflect.Value, x, x+1) + for i := uint8(0); i < x; i++ { + returnValues[i] = reflect.ValueOf(i + 1) + } + if inputErr == noErr { + returnValues = append(returnValues, reflect.Zero(nilErrorType)) + } else if inputErr != nil { + returnValues = append(returnValues, reflect.ValueOf(inputErr)) + } + return returnValues + }) + + if typed { + callArgs = append(callArgs, reflect.ValueOf(catchString)) + } + + fn := reflect.ValueOf(rf).Convert(dynamicFuncType) + result := fn.Call(callArgs) + + // Extract results and error + for i, val := range result[:x] { + results[i] = uint8(val.Uint()) + } + if !result[x].IsNil() { + err = result[x].Interface().(error) + } + + return results, err +} + +func testSuccess(t *testing.T, x uint8, inputErr error, rf recoverFunc, typed bool) { + t.Helper() + expected := createTestValues(x) + is := assert.New(t) + + is.NotPanics(func() { + results, err := callRecover(rf, x, func() {}, inputErr, false, typed) + is.NoError(err) + is.Equal(expected, results) + }, "expected no panic but got one") +} + +func testPanic(t *testing.T, x uint8, inputErr error, rf recoverFunc, typed bool) { + t.Helper() + is := assert.New(t) + + assert.NotPanics(t, func() { + results, err := callRecover(rf, x, func() { panic(panicErr) }, inputErr, false, typed) + is.ErrorIs(err, panicErr) + is.Empty(results) + }, "panic was not caught") +} + +func testError(t *testing.T, x uint8, inputErr error, rf recoverFunc, typed bool) { + t.Helper() + if inputErr == nil { + t.Skip("Error test only applicable to -Error functions") + } + is := assert.New(t) + + results, err := callRecover(rf, x, func() {}, inputErr, false, typed) + + is.Equal(callbackErr, err) + is.Empty(results) +} + +func testUnconfined(t *testing.T, x uint8, inputErr error, rf recoverFunc, typed bool) { + t.Helper() + + assert.PanicsWithValue(t, 1, func() { + _, _ = callRecover(rf, x, func() { panic(1) }, inputErr, false, typed) + }, "expected unconfined error panic but got nothing or wrong panic value") +} + +func testStringPanic(t *testing.T, x uint8, inputErr error, rf recoverFunc, typed bool) { + t.Helper() + is := assert.New(t) + + is.NotPanics(func() { + results, err := callRecover(rf, x, func() { panic("string panic") }, inputErr, true, typed) + is.EqualError(err, "string panic") + is.Empty(results) + }, "expected string panic to be redirected to error return") +} + +func testStringUnconfined(t *testing.T, x uint8, inputErr error, rf recoverFunc, typed bool) { + t.Helper() + is := assert.New(t) + + is.PanicsWithValue("string panic", func() { + results, err := callRecover(rf, x, func() { panic("string panic") }, inputErr, false, typed) + is.NoError(err) + is.Empty(results) + }, "expected unconfined string panic but got nothing or wrong panic value") +} + +func createTestValues(x uint8) []uint8 { + values := make([]uint8, x) + for i := uint8(0); i < x; i++ { + values[i] = i + 1 + } + return values +} + +func TestRecoverXTyped(t *testing.T) { + t.Parallel() + + testMapping := map[testParams][]struct { + inputErr error + testFunc testFuncName + }{ + { + typed: false, + withError: false, + }: { + { + inputErr: nil, + testFunc: "Success", + }, + { + inputErr: nil, + testFunc: "Panic", + }, + { + inputErr: nil, + testFunc: "Unconfined", + }, + { + inputErr: nil, + testFunc: "StringPanic", + }, + }, + { + typed: false, + withError: true, + }: { + { + inputErr: noErr, + testFunc: "Success", + }, + { + inputErr: noErr, + testFunc: "Panic", + }, + { + inputErr: callbackErr, + testFunc: "Error", + }, + { + inputErr: noErr, + testFunc: "Unconfined", + }, + { + inputErr: noErr, + testFunc: "StringPanic", + }, + }, + { + typed: true, + withError: false, + }: { + { + inputErr: nil, + testFunc: "Success", + }, + { + inputErr: nil, + testFunc: "Panic", + }, + { + inputErr: nil, + testFunc: "Unconfined", + }, + { + inputErr: nil, + testFunc: "StringPanic", + }, + { + inputErr: nil, + testFunc: "StringUnconfined", + }, + }, + { + typed: true, + withError: true, + }: { + { + inputErr: noErr, + testFunc: "Success", + }, + { + inputErr: noErr, + testFunc: "Panic", + }, + { + inputErr: callbackErr, + testFunc: "Error", + }, + { + inputErr: noErr, + testFunc: "Unconfined", + }, + { + inputErr: noErr, + testFunc: "StringPanic", + }, + { + inputErr: noErr, + testFunc: "StringUnconfined", + }, + }, + } + + for targetName, testTarget := range functionMapping { + t.Run(string(targetName), func(t *testing.T) { + t.Parallel() + testFuncs := testMapping[testTarget.params] + for _, testFuncParams := range testFuncs { + runName := fmt.Sprintf("%s/%s", targetName, testFuncParams.testFunc) + testFunc := testFuncMapping[testFuncParams.testFunc] + t.Run(runName, func(t *testing.T) { + t.Parallel() + testFunc(t, testTarget.params.x, testFuncParams.inputErr, testTarget.recoverFunc, testTarget.params.typed) + }) + } + }) + } +} From c3ca55724e69c9ef38572d563f51b72709bc25f8 Mon Sep 17 00:00:00 2001 From: Matthias Fax Date: Fri, 27 Sep 2024 10:02:30 +0000 Subject: [PATCH 2/4] docs(README): added section for TypedRecover function --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 4ec044c6..857e312c 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,7 @@ Error handling: - [TryCatch](#trycatch) - [TryWithErrorValue](#trywitherrorvalue) - [TryCatchWithErrorValue](#trycatchwitherrorvalue) +- [TypedRecover](#typedrecover) - [Recover0 -> Recover6](#recover0-6) - [Recover0Error -> Recover6Error](#recover0-6error) - [Recover0Typed -> Recover6Typed](#recover0-6typed) @@ -3469,6 +3470,19 @@ ok := lo.TryCatchWithErrorValue(func() error { [[play](https://go.dev/play/p/8Pc9gwX_GZO)] +### TypedRecover + +`TypedRecover` is a helper function that allows a deferred panic recovery as a one-liner with a type parameter for the error interface and the optional inclusion of string panics via the second parameter flag. + +```go +func ExampleTypedRecover() (err error) { + defer TypedRecover[error](&err, true) + + // Simulating a panic to be returned as error + panic("Unexpected error occurred") +} +``` + ### Recover{0->6} The `Recover{0->6}` functions are designed to encapsulate panicking functions, redirecting panic `error` interfaces and `string` types to golang's idiomatic return signature. This is helpful with library functions where an error types has to be returned in addition to the existing return types. From 9298374758382a9060660ba6d8187dcd6465dfe2 Mon Sep 17 00:00:00 2001 From: Matthias Fax Date: Fri, 27 Sep 2024 10:25:12 +0000 Subject: [PATCH 3/4] test: added examples for Recover functions --- recover_example_test.go | 265 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 recover_example_test.go diff --git a/recover_example_test.go b/recover_example_test.go new file mode 100644 index 00000000..e81fcae4 --- /dev/null +++ b/recover_example_test.go @@ -0,0 +1,265 @@ +package lo + +import ( + "errors" + "fmt" +) + +func ExampleRecover0() { + err := Recover0(func() { + panic("something went wrong") + }) + fmt.Println(err) + // Output: something went wrong +} + +func ExampleRecover0Error() { + err := Recover0Error(func() error { + return errors.New("regular error") + }) + fmt.Println(err) + // Output: regular error +} + +func ExampleRecover1() { + result, err := Recover1(func() int { + panic("calculation error") + return 42 + }) + fmt.Println(result, err) + // Output: 0 calculation error +} + +func ExampleRecover1Error() { + result, err := Recover1Error(func() (int, error) { + return 0, errors.New("division by zero") + }) + fmt.Println(result, err) + // Output: 0 division by zero +} + +func ExampleRecover2() { + x, y, err := Recover2(func() (int, string) { + panic("unexpected input") + return 10, "hello" + }) + fmt.Println(x, y, err) + // Output: 0 unexpected input +} + +func ExampleRecover2Error() { + x, y, err := Recover2Error(func() (int, string, error) { + return 0, "", errors.New("invalid operation") + }) + fmt.Println(x, y, err) + // Output: 0 invalid operation +} + +func ExampleRecover3() { + a, b, c, err := Recover3(func() (int, string, bool) { + panic("critical failure") + return 1, "test", true + }) + fmt.Println(a, b, c, err) + // Output: 0 false critical failure +} + +func ExampleRecover3Error() { + a, b, c, err := Recover3Error(func() (int, string, bool, error) { + return 0, "", false, errors.New("operation failed") + }) + fmt.Println(a, b, c, err) + // Output: 0 false operation failed +} + +func ExampleRecover4() { + w, x, y, z, err := Recover4(func() (int, string, bool, float64) { + panic("system crash") + return 1, "test", true, 3.14 + }) + fmt.Println(w, x, y, z, err) + // Output: 0 false 0 system crash +} + +func ExampleRecover4Error() { + w, x, y, z, err := Recover4Error(func() (int, string, bool, float64, error) { + return 0, "", false, 0.0, errors.New("calculation error") + }) + fmt.Println(w, x, y, z, err) + // Output: 0 false 0 calculation error +} + +func ExampleRecover5() { + v, w, x, y, z, err := Recover5(func() (int, string, bool, float64, []int) { + panic("out of memory") + return 1, "test", true, 3.14, []int{1, 2, 3} + }) + fmt.Println(v, w, x, y, z, err) + // Output: 0 false 0 [] out of memory +} + +func ExampleRecover5Error() { + v, w, x, y, z, err := Recover5Error(func() (int, string, bool, float64, []int, error) { + return 0, "", false, 0.0, nil, errors.New("invalid input") + }) + fmt.Println(v, w, x, y, z, err) + // Output: 0 false 0 [] invalid input +} + +func ExampleRecover6() { + u, v, w, x, y, z, err := Recover6(func() (int, string, bool, float64, []int, map[string]int) { + panic("stack overflow") + return 1, "test", true, 3.14, []int{1, 2, 3}, map[string]int{"a": 1} + }) + fmt.Println(u, v, w, x, y, z, err) + // Output: 0 false 0 [] map[] stack overflow +} + +func ExampleRecover6Error() { + u, v, w, x, y, z, err := Recover6Error(func() (int, string, bool, float64, []int, map[string]int, error) { + return 0, "", false, 0.0, nil, nil, errors.New("operation timeout") + }) + fmt.Println(u, v, w, x, y, z, err) + // Output: 0 false 0 [] map[] operation timeout +} + +func ExampleTypedRecover() { + nestedMyErrorPanic := func() (err error) { + defer TypedRecover[myError](&err, false) + panic(myError{}) + } + err := nestedMyErrorPanic() + + nestedStringPanic := func() (err error) { + defer TypedRecover[error](&err, true) + panic("test string panic") + } + err2 := nestedStringPanic() + + fmt.Println(err, err2) + // Output: my error test string panic +} + +func ExampleRecover0Typed() { + err := Recover0Typed[myError](func() { + panic(myError{}) + }) + fmt.Println(err) + // Output: my error +} + +func ExampleRecover0ErrorTyped() { + err := Recover0ErrorTyped[myError](func() error { + panic("critical failure") + }, true) + fmt.Println(err) + // Output: critical failure +} + +func ExampleRecover1Typed() { + result, err := Recover1Typed[string, error](func() string { + panic("processing error") + return "success" + }, true) + fmt.Println(result, err) + // Output: processing error +} + +func ExampleRecover1ErrorTyped() { + result, err := Recover1ErrorTyped[string, error](func() (string, error) { + panic("processing error") + return "success", nil + }, true) + fmt.Println(result, err) + // Output: processing error +} + +func ExampleRecover2Typed() { + a, b, err := Recover2Typed[int, string, error](func() (int, string) { + panic("invalid input") + return 10, "hello" + }, true) + fmt.Println(a, b, err) + // Output: 0 invalid input +} + +func ExampleRecover2ErrorTyped() { + x, y, err := Recover2ErrorTyped[bool, float64, error](func() (bool, float64, error) { + panic("unexpected condition") + return true, 3.14, nil + }, true) + fmt.Println(x, y, err) + // Output: false 0 unexpected condition +} + +func ExampleRecover3Typed() { + a, b, c, err := Recover3Typed[int, string, bool, error](func() (int, string, bool) { + panic("data corruption") + return 42, "test", true + }, true) + fmt.Println(a, b, c, err) + // Output: 0 false data corruption +} + +func ExampleRecover3ErrorTyped() { + x, y, z, err := Recover3ErrorTyped[int, string, bool, error](func() (int, string, bool, error) { + panic("calculation error") + return 1, "two", true, nil + }, true) + fmt.Println(x, y, z, err) + // Output: 0 false calculation error +} + +func ExampleRecover4Typed() { + a, b, c, d, err := Recover4Typed[int, string, bool, float64, error](func() (int, string, bool, float64) { + panic("unexpected state") + return 1, "test", true, 3.14 + }, true) + fmt.Println(a, b, c, d, err) + // Output: 0 false 0 unexpected state +} + +func ExampleRecover4ErrorTyped() { + w, x, y, z, err := Recover4ErrorTyped[int, string, bool, float64, error](func() (int, string, bool, float64, error) { + panic("division by zero") + return 1, "two", true, 3.14, nil + }, true) + fmt.Println(w, x, y, z, err) + // Output: 0 false 0 division by zero +} + +func ExampleRecover5Typed() { + a, b, c, d, e, err := Recover5Typed[int, string, bool, float64, []int, error](func() (int, string, bool, float64, []int) { + panic("memory allocation failure") + return 1, "test", true, 3.14, []int{1, 2, 3} + }, true) + fmt.Println(a, b, c, d, e, err) + // Output: 0 false 0 [] memory allocation failure +} + +func ExampleRecover5ErrorTyped() { + v, w, x, y, z, err := Recover5ErrorTyped[int, string, bool, float64, []int, error](func() (int, string, bool, float64, []int, error) { + panic("stack overflow") + return 1, "two", true, 3.14, []int{1, 2, 3}, nil + }, true) + fmt.Println(v, w, x, y, z, err) + // Output: 0 false 0 [] stack overflow +} + +func ExampleRecover6Typed() { + a, b, c, d, e, f, err := Recover6Typed[int, string, bool, float64, []int, map[string]int, error](func() (int, string, bool, float64, []int, map[string]int) { + panic("network failure") + return 1, "test", true, 3.14, []int{1, 2, 3}, map[string]int{"a": 1} + }, true) + fmt.Println(a, b, c, d, e, f, err) + // Output: 0 false 0 [] map[] network failure +} + +func ExampleRecover6ErrorTyped() { + u, v, w, x, y, z, err := Recover6ErrorTyped[int, string, bool, float64, []int, map[string]int, error](func() (int, string, bool, float64, []int, map[string]int, error) { + panic("database connection lost") + return 1, "two", true, 3.14, []int{1, 2, 3}, map[string]int{"a": 1}, nil + }, true) + fmt.Println(u, v, w, x, y, z, err) + // Output: 0 false 0 [] map[] database connection lost +} From fed1eb42c1178187d039c9867b482ffac0e33b65 Mon Sep 17 00:00:00 2001 From: Matthias Fax Date: Fri, 27 Sep 2024 10:51:42 +0000 Subject: [PATCH 4/4] docs(README): more accurate function description --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 857e312c..28693c4f 100644 --- a/README.md +++ b/README.md @@ -3485,7 +3485,7 @@ func ExampleTypedRecover() (err error) { ### Recover{0->6} -The `Recover{0->6}` functions are designed to encapsulate panicking functions, redirecting panic `error` interfaces and `string` types to golang's idiomatic return signature. This is helpful with library functions where an error types has to be returned in addition to the existing return types. +The `Recover{0->6}` functions are designed to encapsulate panicking functions, redirecting panic `error` interfaces and `string` types to golang's idiomatic return signature. This is helpful with library functions where an error type has to be returned in addition to the existing return types. ```go // Example of a library function @@ -3506,11 +3506,10 @@ errorOrNil(func(param string) string { ### Recover{0->6}Error -These functions merge both `Recover{0->6}` and callback error return types into a unified error type. This can be helpful in situations where non-error inline functions are passed to library functions that come with their own error return, making panic the only workaround to pass errors from the callback function to the encapsulating code. +These functions merge both `Recover{0->6}` and callback error return types into a unified error type. This can be helpful in situations where non-error callback functions are passed to library functions that come with their own error return, making panic the only workaround to pass errors from the callback function to the encapsulating scope. ```go -// This could be a library function -// Example of a library function +// A library function with its own error return and a callback without error handling func externalLibraryFunction(callback func(string) string) (string, error) { _ = callback("test") return "", errors.New("final error")