// 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]() }