146 lines
4.2 KiB
Go
146 lines
4.2 KiB
Go
// MIT License
|
|
|
|
// Copyright (c) 2022 Tom Godkin
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
// o provides an Option type to represent values that may or may not be present.
|
|
//
|
|
// This code was copies from https://github.com/BooleanCat/go-functional@ae5a155c0e997d1c5de53ea8b49109aca9c53d9f
|
|
// and we've added the Map function and associated tests. It was pulled into the project because I believe if we're
|
|
// using Option, it should be a core domain type rather than a dependency.
|
|
package o
|
|
|
|
import "fmt"
|
|
|
|
// Option represents an optional value. The [Some] variant contains a value and
|
|
// the [None] variant represents the absence of a value.
|
|
type Option[T any] struct {
|
|
value T
|
|
present bool
|
|
}
|
|
|
|
// Some instantiates an [Option] with a value.
|
|
func Some[T any](value T) Option[T] {
|
|
return Option[T]{value, true}
|
|
}
|
|
|
|
// None instantiates an [Option] with no value.
|
|
func None[T any]() Option[T] {
|
|
return Option[T]{}
|
|
}
|
|
|
|
func SomeIfNonZero[T comparable](value T) Option[T] {
|
|
// value is a zero value then return a None
|
|
var zero T
|
|
if value == zero {
|
|
return None[T]()
|
|
}
|
|
return Some(value)
|
|
}
|
|
|
|
// String implements the [fmt.Stringer] interface.
|
|
func (o Option[T]) String() string {
|
|
if o.present {
|
|
return fmt.Sprintf("Some(%v)", o.value)
|
|
}
|
|
|
|
return "None"
|
|
}
|
|
|
|
var _ fmt.Stringer = Option[struct{}]{}
|
|
|
|
// Unwrap returns the underlying value of a [Some] variant, or panics if called
|
|
// on a [None] variant.
|
|
func (o Option[T]) Unwrap() T {
|
|
if o.present {
|
|
return o.value
|
|
}
|
|
|
|
panic("called `Option.Unwrap()` on a `None` value")
|
|
}
|
|
|
|
// UnwrapOr returns the underlying value of a [Some] variant, or the provided
|
|
// value on a [None] variant.
|
|
func (o Option[T]) UnwrapOr(value T) T {
|
|
if o.present {
|
|
return o.value
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
// UnwrapOrElse returns the underlying value of a [Some] variant, or the result
|
|
// of calling the provided function on a [None] variant.
|
|
func (o Option[T]) UnwrapOrElse(f func() T) T {
|
|
if o.present {
|
|
return o.value
|
|
}
|
|
|
|
return f()
|
|
}
|
|
|
|
// UnwrapOrZero returns the underlying value of a [Some] variant, or the zero
|
|
// value on a [None] variant.
|
|
func (o Option[T]) UnwrapOrZero() T {
|
|
if o.present {
|
|
return o.value
|
|
}
|
|
|
|
var value T
|
|
return value
|
|
}
|
|
|
|
// IsSome returns true if the [Option] is a [Some] variant.
|
|
func (o Option[T]) IsSome() bool {
|
|
return o.present
|
|
}
|
|
|
|
// IsNone returns true if the [Option] is a [None] variant.
|
|
func (o Option[T]) IsNone() bool {
|
|
return !o.present
|
|
}
|
|
|
|
// Value returns the underlying value and true for a [Some] variant, or the
|
|
// zero value and false for a [None] variant.
|
|
func (o Option[T]) Value() (T, bool) {
|
|
return o.value, o.present
|
|
}
|
|
|
|
// Expect returns the underlying value for a [Some] variant, or panics with the
|
|
// provided message for a [None] variant.
|
|
func (o Option[T]) Expect(message string) T {
|
|
if o.present {
|
|
return o.value
|
|
}
|
|
|
|
panic(message)
|
|
}
|
|
|
|
// Map applies a function to the contained value of (if [Some]), or returns [None].
|
|
//
|
|
// Use this function very sparingly as it can lead to very unidiomatic and surprising Go code. However,
|
|
// there are times when used judiciiously, it is significantly more ergonomic than unwrapping the Option.
|
|
func Map[T, U any](o Option[T], f func(T) U) Option[U] {
|
|
if o.present {
|
|
return Some(f(o.value))
|
|
}
|
|
|
|
return None[U]()
|
|
}
|