github.com/storacha/go-ucanto@v0.7.2/core/result/result.go (about) 1 package result 2 3 import ( 4 "github.com/storacha/go-ucanto/core/ipld" 5 "github.com/storacha/go-ucanto/core/result/failure" 6 "github.com/storacha/go-ucanto/core/result/failure/datamodel" 7 ) 8 9 // Result is a golang compatible generic result type 10 type Result[O any, X any] interface { 11 isResult(ok O, err X) 12 } 13 14 type okResult[O any, X any] struct { 15 value O 16 } 17 type errResult[O any, X any] struct { 18 value X 19 } 20 21 func (o *okResult[O, X]) isResult(ok O, err X) {} 22 func (e *errResult[O, X]) isResult(ok O, err X) {} 23 24 // MatchResultR3 handles a result with functions returning 3 values 25 func MatchResultR3[O any, X any, R0, R1, R2 any]( 26 result Result[O, X], 27 onOk func(ok O) (R0, R1, R2), 28 onError func(err X) (R0, R1, R2), 29 ) (R0, R1, R2) { 30 switch v := result.(type) { 31 case *okResult[O, X]: 32 return onOk(v.value) 33 case *errResult[O, X]: 34 return onError(v.value) 35 default: 36 panic("unexpected result type") 37 } 38 } 39 40 // MatchResultR2 handles a result with functions returning two values 41 func MatchResultR2[O any, X any, R0, R1 any]( 42 result Result[O, X], 43 onOk func(ok O) (R0, R1), 44 onError func(err X) (R0, R1), 45 ) (R0, R1) { 46 switch v := result.(type) { 47 case *okResult[O, X]: 48 return onOk(v.value) 49 case *errResult[O, X]: 50 return onError(v.value) 51 default: 52 panic("unexpected result type") 53 } 54 } 55 56 // MatchResultR1 handles a result with functions returning one value 57 func MatchResultR1[O any, X any, T0 any]( 58 result Result[O, X], 59 onOk func(ok O) T0, 60 onError func(err X) T0, 61 ) T0 { 62 switch v := result.(type) { 63 case *okResult[O, X]: 64 return onOk(v.value) 65 case *errResult[O, X]: 66 return onError(v.value) 67 default: 68 panic("unexpected result type") 69 } 70 } 71 72 // MatchResultR1 handles a result with a functions that has no return value 73 func MatchResultR0[O any, X any]( 74 result Result[O, X], 75 onOk func(ok O), 76 onError func(err X), 77 ) { 78 switch v := result.(type) { 79 case *okResult[O, X]: 80 onOk(v.value) 81 case *errResult[O, X]: 82 onError(v.value) 83 default: 84 panic("unexpected result type") 85 } 86 } 87 88 // Ok returns a success result type 89 func Ok[O, X any](value O) Result[O, X] { 90 return &okResult[O, X]{value} 91 } 92 93 // Error returns an error result type 94 func Error[O, X any](value X) Result[O, X] { 95 return &errResult[O, X]{value} 96 } 97 98 // MapOk transforms a successful result while leaving an error result unchanged 99 func MapOk[O, X, O2 any](result Result[O, X], mapFn func(O) O2) Result[O2, X] { 100 return MapResultR0(result, mapFn, func(err X) X { return err }) 101 } 102 103 // MapError transforms an error result while leaving a success result unchanged 104 func MapError[O, X, X2 any](result Result[O, X], mapFn func(X) X2) Result[O, X2] { 105 return MapResultR0(result, func(ok O) O { return ok }, mapFn) 106 } 107 108 // MapResultR0 transforms a result -- 109 // with seperate functions to modify both the success type and error type 110 func MapResultR0[O, X, O2, X2 any](result Result[O, X], mapOkFn func(O) O2, mapErrFn func(X) X2) Result[O2, X2] { 111 return MatchResultR1(result, func(ok O) Result[O2, X2] { 112 return Ok[O2, X2](mapOkFn(ok)) 113 }, func(err X) Result[O2, X2] { 114 return Error[O2, X2](mapErrFn(err)) 115 }) 116 } 117 118 // MapResultR1 transforms a result -- 119 // with seperate functions to modify both the success type and error type that also returna one additional value 120 func MapResultR1[O, X, O2, X2, R1 any](result Result[O, X], mapOkFn func(O) (O2, R1), mapErrFn func(X) (X2, R1)) (Result[O2, X2], R1) { 121 return MatchResultR2(result, func(ok O) (Result[O2, X2], R1) { 122 ok2, r1 := mapOkFn(ok) 123 return Ok[O2, X2](ok2), r1 124 }, func(err X) (Result[O2, X2], R1) { 125 err2, r1 := mapErrFn(err) 126 return Error[O2, X2](err2), r1 127 }) 128 } 129 130 // And treats a result as a boolean, returning the second result only if the 131 // the first is succcessful 132 func And[O, O2, X any](res1 Result[O, X], res2 Result[O2, X]) Result[O2, X] { 133 return AndThen(res1, func(_ O) Result[O2, X] { return res2 }) 134 } 135 136 // AndThen takes a result and if it is success type, 137 // runs an additional function that returns a subsequent result type 138 func AndThen[O, X, O2 any](result Result[O, X], thenFunc func(O) Result[O2, X]) Result[O2, X] { 139 return MatchResultR1(result, func(ok O) Result[O2, X] { 140 return thenFunc(ok) 141 }, func(err X) Result[O2, X] { 142 return Error[O2, X](err) 143 }) 144 } 145 146 // Or treats a result as a boolean, returning the second result if the first 147 // result is an error 148 func Or[O, X, X2 any](res1 Result[O, X], res2 Result[O, X2]) Result[O, X2] { 149 return OrElse(res1, func(err X) Result[O, X2] { return res2 }) 150 } 151 152 // OrElse takes a result and if it is an error type, 153 // runs an additional function that returns a subsequent result type 154 func OrElse[O, X, X2 any](result Result[O, X], elseFunc func(X) Result[O, X2]) Result[O, X2] { 155 return MatchResultR1(result, func(ok O) Result[O, X2] { 156 return Ok[O, X2](ok) 157 }, func(err X) Result[O, X2] { 158 return elseFunc(err) 159 }) 160 } 161 162 // Wrap wraps a traditional golang pattern for two value functions with the 163 // second being an error where the zero value indicates absence, converting 164 // it to a result 165 func Wrap[O any, X comparable](inner func() (O, X)) Result[O, X] { 166 o, err := inner() 167 var nilErr X 168 if err != nilErr { 169 return Error[O, X](err) 170 } 171 return Ok[O, X](o) 172 } 173 174 func Unwrap[O any, X any](result Result[O, X]) (O, X) { 175 return MatchResultR2(result, func(ok O) (O, X) { 176 var err X 177 return ok, err 178 }, func(err X) (O, X) { 179 var ok O 180 return ok, err 181 }) 182 } 183 184 func NewFailure(err error) Result[ipld.Builder, ipld.Builder] { 185 if ipldConvertableError, ok := err.(failure.IPLDConvertableError); ok { 186 return Error[ipld.Builder, ipld.Builder](ipldConvertableError) 187 } 188 189 model := datamodel.FailureModel{Message: err.Error()} 190 if named, ok := err.(failure.Named); ok { 191 name := named.Name() 192 model.Name = &name 193 } 194 if withStackTrace, ok := err.(failure.WithStackTrace); ok { 195 stack := withStackTrace.Stack() 196 model.Stack = &stack 197 } 198 return Error[ipld.Builder, ipld.Builder](&model) 199 } 200 201 // https://en.wikipedia.org/wiki/Unit_type 202 type Unit interface{}