feat(Type): add new function to be able to recover from panics when unwrapping a none/error value
This commit is contained in:
@@ -11,6 +11,10 @@ type ValueContainer interface {
|
|||||||
HasValue() bool
|
HasValue() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Recoverable[T any] interface {
|
||||||
|
CatchUnwrap(T)
|
||||||
|
}
|
||||||
|
|
||||||
type Unwrappable[T any] interface {
|
type Unwrappable[T any] interface {
|
||||||
Expect(string) T // panics with a provided custom message
|
Expect(string) T // panics with a provided custom message
|
||||||
Unwrap() T // panics with a generic message
|
Unwrap() T // panics with a generic message
|
||||||
@@ -30,7 +34,8 @@ type Optionaler[T any] interface {
|
|||||||
IsNone() bool
|
IsNone() bool
|
||||||
OkOr(error) Result[T]
|
OkOr(error) Result[T]
|
||||||
OkOrElse(func() error) Result[T]
|
OkOrElse(func() error) Result[T]
|
||||||
Unwrappable[T]
|
Optioner[T]
|
||||||
|
OptionalerMarker
|
||||||
}
|
}
|
||||||
|
|
||||||
type Resulter[T any] interface {
|
type Resulter[T any] interface {
|
||||||
@@ -38,11 +43,24 @@ type Resulter[T any] interface {
|
|||||||
IsErr() bool
|
IsErr() bool
|
||||||
Ok() Optional[T]
|
Ok() Optional[T]
|
||||||
Err() Optional[error]
|
Err() Optional[error]
|
||||||
Unwrappable[T]
|
Optioner[T]
|
||||||
|
ResulterMarker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marker interfaces to help type matching
|
||||||
|
type (
|
||||||
|
ResulterMarker interface {
|
||||||
|
Result()
|
||||||
|
}
|
||||||
|
OptionalerMarker interface {
|
||||||
|
Optional()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Ensure compile time the interfaces are implemented
|
// Ensure compile time the interfaces are implemented
|
||||||
var (
|
var (
|
||||||
|
_ OptionalerMarker = (*Optional[any])(nil)
|
||||||
|
_ ResulterMarker = (*Result[any])(nil)
|
||||||
_ Optioner[any] = (*Optional[any])(nil)
|
_ Optioner[any] = (*Optional[any])(nil)
|
||||||
_ Optioner[any] = (*Result[any])(nil)
|
_ Optioner[any] = (*Result[any])(nil)
|
||||||
_ Optionaler[any] = (*Optional[any])(nil)
|
_ Optionaler[any] = (*Optional[any])(nil)
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import (
|
|||||||
Assert "github.com/lbatuska/goutils/assert"
|
Assert "github.com/lbatuska/goutils/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Marker interface impl
|
||||||
|
func (opt Optional[T]) Optional() {}
|
||||||
|
|
||||||
// CTORS BEGIN
|
// CTORS BEGIN
|
||||||
func Some[T any](value T) Optional[T] {
|
func Some[T any](value T) Optional[T] {
|
||||||
return Optional[T]{value, true}
|
return Optional[T]{value, true}
|
||||||
@@ -57,11 +60,14 @@ func (opt Optional[T]) Expect(msg string) T {
|
|||||||
panic(msg)
|
panic(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt Optional[T]) Unwrap() T {
|
func (opt *Optional[T]) Unwrap() T {
|
||||||
|
if opt == nil {
|
||||||
|
panic("Tried unwrapping an Optional that did not have a value!")
|
||||||
|
}
|
||||||
if opt.present {
|
if opt.present {
|
||||||
return opt.value
|
return opt.value
|
||||||
}
|
}
|
||||||
panic("Tried unwrapping an Optional that did not have a value!")
|
panic(opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt *Optional[T]) UnwrapOr(val T) T {
|
func (opt *Optional[T]) UnwrapOr(val T) T {
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import (
|
|||||||
Assert "github.com/lbatuska/goutils/assert"
|
Assert "github.com/lbatuska/goutils/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Marker interface impl
|
||||||
|
func (res Result[T]) Result() {}
|
||||||
|
|
||||||
// CTORS BEGIN
|
// CTORS BEGIN
|
||||||
func Ok[T any](value T) Result[T] {
|
func Ok[T any](value T) Result[T] {
|
||||||
return Result[T]{value: value, err: nil}
|
return Result[T]{value: value, err: nil}
|
||||||
@@ -56,11 +59,14 @@ func (res Result[T]) Expect(msg string) T {
|
|||||||
panic(msg)
|
panic(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (res Result[T]) Unwrap() T {
|
func (res *Result[T]) Unwrap() T {
|
||||||
|
if res == nil {
|
||||||
|
panic("Tried unwrapping a Result that had an error value!")
|
||||||
|
}
|
||||||
if res.err == nil {
|
if res.err == nil {
|
||||||
return res.value
|
return res.value
|
||||||
}
|
}
|
||||||
panic("Tried unwrapping a Result that had an error value!")
|
panic(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (res *Result[T]) UnwrapOr(val T) T {
|
func (res *Result[T]) UnwrapOr(val T) T {
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
package Type
|
package Type
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
func Expect[T any](val Unwrappable[T], msg string) T {
|
func Expect[T any](val Unwrappable[T], msg string) T {
|
||||||
return val.Expect(msg)
|
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 {
|
func Ptr[T any](v T) *T {
|
||||||
return &v
|
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