mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
231 lines
5.5 KiB
Go
231 lines
5.5 KiB
Go
package fn
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Result represents a value that can either be a success (T) or an error.
|
|
type Result[T any] struct {
|
|
Either[T, error]
|
|
}
|
|
|
|
// NewResult creates a new result from a (value, error) tuple.
|
|
func NewResult[T any](val T, err error) Result[T] {
|
|
if err != nil {
|
|
return Err[T](err)
|
|
}
|
|
|
|
return Ok(val)
|
|
}
|
|
|
|
// Ok creates a new Result with a success value.
|
|
func Ok[T any](val T) Result[T] {
|
|
return Result[T]{Either: NewLeft[T, error](val)}
|
|
}
|
|
|
|
// Err creates a new Result with an error.
|
|
func Err[T any](err error) Result[T] {
|
|
return Result[T]{Either: NewRight[T, error](err)}
|
|
}
|
|
|
|
// Errf creates a new Result with a new formatted error string.
|
|
func Errf[T any](errString string, args ...any) Result[T] {
|
|
return Result[T]{
|
|
Either: NewRight[T, error](fmt.Errorf(errString, args...)),
|
|
}
|
|
}
|
|
|
|
// Unpack extracts the value or error from the Result.
|
|
func (r Result[T]) Unpack() (T, error) {
|
|
var zero T
|
|
|
|
if r.IsErr() {
|
|
return zero, r.right
|
|
}
|
|
|
|
return r.left, nil
|
|
}
|
|
|
|
// Err exposes the underlying error of the result type as a normal error type.
|
|
func (r Result[T]) Err() error {
|
|
return r.right
|
|
}
|
|
|
|
// IsOk returns true if the Result is a success value.
|
|
func (r Result[T]) IsOk() bool {
|
|
return r.IsLeft()
|
|
}
|
|
|
|
// IsErr returns true if the Result is an error.
|
|
func (r Result[T]) IsErr() bool {
|
|
return r.IsRight()
|
|
}
|
|
|
|
// MapOk applies an endomorphic function to the success value if it exists.
|
|
func (r Result[T]) MapOk(f func(T) T) Result[T] {
|
|
return Result[T]{
|
|
MapLeft[T, error](f)(r.Either),
|
|
}
|
|
}
|
|
|
|
// MapErr applies an endomorphic function to the error value if it exists.
|
|
func (r Result[T]) MapErr(f func(error) error) Result[T] {
|
|
return Result[T]{
|
|
MapRight[T](f)(r.Either),
|
|
}
|
|
}
|
|
|
|
// MapOk applies a non-endomorphic function to the success value if it exists
|
|
// and returns a Result of the new type.
|
|
func MapOk[A, B any](f func(A) B) func(Result[A]) Result[B] {
|
|
return func(r Result[A]) Result[B] {
|
|
return Result[B]{MapLeft[A, error](f)(r.Either)}
|
|
}
|
|
}
|
|
|
|
// OkToSome mutes the error value of the result.
|
|
func (r Result[T]) OkToSome() Option[T] {
|
|
return r.Either.LeftToSome()
|
|
}
|
|
|
|
// WhenOk executes the given function if the Result is a success.
|
|
func (r Result[T]) WhenOk(f func(T)) {
|
|
r.WhenLeft(f)
|
|
}
|
|
|
|
// WhenErr executes the given function if the Result is an error.
|
|
func (r Result[T]) WhenErr(f func(error)) {
|
|
r.WhenRight(f)
|
|
}
|
|
|
|
// UnwrapOr returns the success value or a default value if it's an error.
|
|
func (r Result[T]) UnwrapOr(defaultValue T) T {
|
|
if r.IsErr() {
|
|
return defaultValue
|
|
}
|
|
|
|
return r.left
|
|
}
|
|
|
|
// UnwrapOrElse returns the success value or computes a value from a function
|
|
// if it's an error.
|
|
func (r Result[T]) UnwrapOrElse(f func(error) T) T {
|
|
if r.IsErr() {
|
|
return f(r.right)
|
|
}
|
|
|
|
return r.left
|
|
}
|
|
|
|
// UnwrapOrFail returns the success value or fails the test if it's an error.
|
|
func (r Result[T]) UnwrapOrFail(t *testing.T) T {
|
|
t.Helper()
|
|
|
|
require.True(
|
|
t, r.IsOk(), "Result[%T] contained error: %v", r.left, r.right,
|
|
)
|
|
|
|
return r.left
|
|
}
|
|
|
|
// FlattenResult takes a nested Result and joins the two functor layers into
|
|
// one.
|
|
func FlattenResult[A any](r Result[Result[A]]) Result[A] {
|
|
if r.IsErr() {
|
|
return Err[A](r.right)
|
|
}
|
|
|
|
if r.left.IsErr() {
|
|
return Err[A](r.left.right)
|
|
}
|
|
|
|
return r.left
|
|
}
|
|
|
|
// FlatMap applies a kleisli endomorphic function that returns a Result to the
|
|
// success value if it exists.
|
|
func (r Result[T]) FlatMap(f func(T) Result[T]) Result[T] {
|
|
if r.IsOk() {
|
|
return r
|
|
}
|
|
|
|
return f(r.left)
|
|
}
|
|
|
|
// AndThen is an alias for FlatMap. This along with OrElse can be used to
|
|
// Railway Oriented Programming (ROP) by chaining successive computational
|
|
// operations from a single result type.
|
|
func (r Result[T]) AndThen(f func(T) Result[T]) Result[T] {
|
|
return r.FlatMap(f)
|
|
}
|
|
|
|
// OrElse returns the original Result if it is a success, otherwise it returns
|
|
// the provided alternative Result. This along with AndThen can be used to
|
|
// Railway Oriented Programming (ROP).
|
|
func (r Result[T]) OrElse(f func(error) Result[T]) Result[T] {
|
|
if r.IsOk() {
|
|
return r
|
|
}
|
|
|
|
return f(r.right)
|
|
}
|
|
|
|
// FlatMapResult applies a function that returns a Result[B] to the success
|
|
// value if it exists.
|
|
func FlatMapResult[A, B any](r Result[A], f func(A) Result[B]) Result[B] {
|
|
if r.IsOk() {
|
|
return f(r.left)
|
|
}
|
|
|
|
return Err[B](r.right)
|
|
}
|
|
|
|
// AndThen is an alias for FlatMap. This along with OrElse can be used to
|
|
// Railway Oriented Programming (ROP).
|
|
func AndThen[A, B any](r Result[A], f func(A) Result[B]) Result[B] {
|
|
return FlatMapResult(r, f)
|
|
}
|
|
|
|
// LiftA2Result lifts a two-argument function to a function that can operate
|
|
// over results of its arguments.
|
|
func LiftA2Result[A, B, C any](f func(A, B) C,
|
|
) func(Result[A], Result[B]) Result[C] {
|
|
|
|
return func(ra Result[A], rb Result[B]) Result[C] {
|
|
if ra.IsErr() {
|
|
return Err[C](ra.right)
|
|
}
|
|
|
|
if rb.IsErr() {
|
|
return Err[C](rb.right)
|
|
}
|
|
|
|
return Ok(f(ra.left, rb.left))
|
|
}
|
|
}
|
|
|
|
// Sink consumes a Result, either propagating its error or processing its
|
|
// success value with a function that can fail.
|
|
func (r Result[A]) Sink(f func(A) error) error {
|
|
if r.IsErr() {
|
|
return r.right
|
|
}
|
|
|
|
return f(r.left)
|
|
}
|
|
|
|
// TransposeResOpt transposes the Result[Option[A]] into a Option[Result[A]].
|
|
// This has the effect of leaving an A value alone while inverting the Result
|
|
// and Option layers. If there is no internal A value, it will convert the
|
|
// non-success value to the proper one in the transposition.
|
|
func TransposeResOpt[A any](r Result[Option[A]]) Option[Result[A]] {
|
|
if r.IsErr() {
|
|
return Some(Err[A](r.right))
|
|
}
|
|
|
|
return MapOption(Ok[A])(r.left)
|
|
}
|