feat(Type): add new function to be able to recover from panics when unwrapping a none/error value
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
package Type
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Expect[T any](val Unwrappable[T], msg string) T {
|
||||
return val.Expect(msg)
|
||||
}
|
||||
@@ -42,3 +48,92 @@ func ResultWrapb[T any](err error, val T) Result[T] {
|
||||
func Ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
// meant to be used as defer Type.CatchUnwrap(Type.Ptr(&res)) or Type.CatchUnwrap(&res) if res is already a pointer
|
||||
// where res is a pointer to an Option or Result returned by a function (initialized to not be nil)
|
||||
// func X() (res *Optional[int]) {
|
||||
// res = None[int]()
|
||||
// defer CatchUnwrap(&res)
|
||||
//
|
||||
// // Some possibly unsafe unwrapping of values
|
||||
// return res
|
||||
// }
|
||||
// === OR ===
|
||||
// func X() (res Optional[int]) {
|
||||
// res = None[int]()
|
||||
// defer Type.CatchUnwrap(Type.Ptr(&res))
|
||||
//
|
||||
// // Some possibly unsafe unwrapping of values
|
||||
// return res
|
||||
// }
|
||||
|
||||
func CatchUnwrap(ret interface{}) {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
vp := reflect.ValueOf(r)
|
||||
if vp.Kind() != reflect.Pointer || vp.IsNil() {
|
||||
panic(r)
|
||||
}
|
||||
|
||||
if _, ok := r.(OptionalerMarker); ok {
|
||||
if setOptionalNone(ret) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := r.(ResulterMarker); ok {
|
||||
if setResultError(ret, fmt.Errorf("Tried to unwrap a failed result!")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
panic(r)
|
||||
}
|
||||
|
||||
func setOptionalNone(ret interface{}) bool {
|
||||
v := reflect.ValueOf(ret)
|
||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||
return false
|
||||
}
|
||||
elem := v.Elem().Elem()
|
||||
if elem.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
presentField := elem.FieldByName("present")
|
||||
if !presentField.IsValid() {
|
||||
return false
|
||||
}
|
||||
if presentField.Kind() == reflect.Bool {
|
||||
presentField = reflect.NewAt(presentField.Type(),
|
||||
unsafe.Pointer(presentField.UnsafeAddr())).Elem()
|
||||
presentField.SetBool(false) // Setting it to false (None)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func setResultError(ret interface{}, err error) bool {
|
||||
v := reflect.ValueOf(ret)
|
||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||
return false
|
||||
}
|
||||
elem := v.Elem().Elem()
|
||||
if elem.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
errField := elem.FieldByName("err")
|
||||
if !errField.IsValid() {
|
||||
return false
|
||||
}
|
||||
if errField.Kind() == reflect.Interface {
|
||||
errField = reflect.NewAt(errField.Type(),
|
||||
unsafe.Pointer(errField.UnsafeAddr())).Elem()
|
||||
errField.Set(reflect.ValueOf(err)) // Setting the error
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user