qvm/vendor/github.com/samber/mo/result.go
Joshua Bell f501abe660 vendor
2026-01-26 21:41:26 -06:00

245 lines
6.1 KiB
Go

package mo
import (
"encoding/json"
"errors"
"fmt"
)
// Ok builds a Result when value is valid.
// Play: https://go.dev/play/p/PDwADdzNoyZ
func Ok[T any](value T) Result[T] {
return Result[T]{
value: value,
isErr: false,
}
}
// Err builds a Result when value is invalid.
// Play: https://go.dev/play/p/PDwADdzNoyZ
func Err[T any](err error) Result[T] {
return Result[T]{
err: err,
isErr: true,
}
}
// Errf builds a Result when value is invalid.
// Errf formats according to a format specifier and returns the error as a value that satisfies Result[T].
// Play: https://go.dev/play/p/N43w92SM-Bs
func Errf[T any](format string, a ...any) Result[T] {
return Err[T](fmt.Errorf(format, a...))
}
// TupleToResult convert a pair of T and error into a Result.
// Play: https://go.dev/play/p/KWjfqQDHQwa
func TupleToResult[T any](value T, err error) Result[T] {
if err != nil {
return Err[T](err)
}
return Ok(value)
}
// Try returns either a Ok or Err object.
// Play: https://go.dev/play/p/ilOlQx-Mx42
func Try[T any](f func() (T, error)) Result[T] {
return TupleToResult(f())
}
// Result represents a result of an action having one
// of the following output: success or failure.
// An instance of Result is an instance of either Ok or Err.
// It could be compared to `Either[error, T]`.
type Result[T any] struct {
isErr bool
value T
err error
}
// IsOk returns true when value is valid.
// Play: https://go.dev/play/p/sfNvBQyZfgU
func (r Result[T]) IsOk() bool {
return !r.isErr
}
// IsError returns true when value is invalid.
// Play: https://go.dev/play/p/xkV9d464scV
func (r Result[T]) IsError() bool {
return r.isErr
}
// Error returns error when value is invalid or nil.
// Play: https://go.dev/play/p/CSkHGTyiXJ5
func (r Result[T]) Error() error {
return r.err
}
// Get returns value and error.
// Play: https://go.dev/play/p/8KyX3z6TuNo
func (r Result[T]) Get() (T, error) {
if r.isErr {
return empty[T](), r.err
}
return r.value, nil
}
// MustGet returns value when Result is valid or panics.
// Play: https://go.dev/play/p/8LSlndHoTAE
func (r Result[T]) MustGet() T {
if r.isErr {
panic(r.err)
}
return r.value
}
// OrElse returns value when Result is valid or default value.
// Play: https://go.dev/play/p/MN_ULx0soi6
func (r Result[T]) OrElse(fallback T) T {
if r.isErr {
return fallback
}
return r.value
}
// OrEmpty returns value when Result is valid or empty value.
// Play: https://go.dev/play/p/rdKtBmOcMLh
func (r Result[T]) OrEmpty() T {
return r.value
}
// ToEither transforms a Result into an Either type.
// Play: https://go.dev/play/p/Uw1Zz6b952q
func (r Result[T]) ToEither() Either[error, T] {
if r.isErr {
return Left[error, T](r.err)
}
return Right[error, T](r.value)
}
// ForEach executes the given side-effecting function if Result is valid.
func (r Result[T]) ForEach(mapper func(value T)) {
if !r.isErr {
mapper(r.value)
}
}
// Match executes the first function if Result is valid and second function if invalid.
// It returns a new Result.
// Play: https://go.dev/play/p/-_eFaLJ31co
func (r Result[T]) Match(onSuccess func(value T) (T, error), onError func(err error) (T, error)) Result[T] {
if r.isErr {
return TupleToResult(onError(r.err))
}
return TupleToResult(onSuccess(r.value))
}
// Map executes the mapper function if Result is valid. It returns a new Result.
// Play: https://go.dev/play/p/-ndpN_b_OSc
func (r Result[T]) Map(mapper func(value T) (T, error)) Result[T] {
if !r.isErr {
return TupleToResult(mapper(r.value))
}
return Err[T](r.err)
}
// MapValue executes the mapper function if Result is valid. It returns a new Result.
func (r Result[T]) MapValue(mapper func(value T) T) Result[T] {
if !r.isErr {
return TupleToResult(mapper(r.value), nil)
}
return Err[T](r.err)
}
// MapErr executes the mapper function if Result is invalid. It returns a new Result.
// Play: https://go.dev/play/p/WraZixg9GGf
func (r Result[T]) MapErr(mapper func(error) (T, error)) Result[T] {
if r.isErr {
return TupleToResult(mapper(r.err))
}
return Ok(r.value)
}
// FlatMap executes the mapper function if Result is valid. It returns a new Result.
// Play: https://go.dev/play/p/Ud5QjZOqg-7
func (r Result[T]) FlatMap(mapper func(value T) Result[T]) Result[T] {
if !r.isErr {
return mapper(r.value)
}
return Err[T](r.err)
}
// MarshalJSON encodes Result into json, following the JSON-RPC specification for results,
// with one exception: when the result is an error, the "code" field is not included.
// Reference: https://www.jsonrpc.org/specification
func (o Result[T]) MarshalJSON() ([]byte, error) {
if o.isErr {
return json.Marshal(map[string]any{
"error": map[string]any{
"message": o.err.Error(),
},
})
}
return json.Marshal(map[string]any{
"result": o.value,
})
}
// UnmarshalJSON decodes json into Result. If "error" is set, the result is an
// Err containing the error message as a generic error object. Otherwise, the
// result is an Ok containing the result. If the JSON object contains netiher
// an error nor a result, the result is an Ok containing an empty value. If the
// JSON object contains both an error and a result, the result is an Err. Finally,
// if the JSON object contains an error but is not structured correctly (no message
// field), the unmarshaling fails.
func (o *Result[T]) UnmarshalJSON(data []byte) error {
var result struct {
Result T `json:"result"`
Error struct {
Message string `json:"message"`
} `json:"error"`
}
if err := json.Unmarshal(data, &result); err != nil {
return err
}
if result.Error.Message != "" {
o.err = errors.New(result.Error.Message)
o.isErr = true
return nil
}
o.value = result.Result
o.isErr = false
return nil
}
// leftValue returns the error if the Result is an error, otherwise nil
//
//nolint:unused
func (r Result[T]) leftValue() error {
return r.err
}
// rightValue returns the value if the Result is a success, otherwise the zero value of T
//
//nolint:unused
func (r Result[T]) rightValue() T {
return r.value
}
// hasLeftValue returns true if the Result represents an error state.
//
//nolint:unused
func (r Result[T]) hasLeftValue() bool {
return r.isErr
}