github.com/moznion/go-optional@v0.11.1-0.20240312043125-6881072e44c1/option.go (about)

     1  package optional
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  )
     9  
    10  var (
    11  	// ErrNoneValueTaken represents the error that is raised when None value is taken.
    12  	ErrNoneValueTaken = errors.New("none value taken")
    13  )
    14  
    15  // Option is a data type that must be Some (i.e. having a value) or None (i.e. doesn't have a value).
    16  // This type implements database/sql/driver.Valuer and database/sql.Scanner.
    17  type Option[T any] []T
    18  
    19  const (
    20  	value = iota
    21  )
    22  
    23  // Some is a function to make an Option type value with the actual value.
    24  func Some[T any](v T) Option[T] {
    25  	return Option[T]{
    26  		value: v,
    27  	}
    28  }
    29  
    30  // None is a function to make an Option type value that doesn't have a value.
    31  func None[T any]() Option[T] {
    32  	return nil
    33  }
    34  
    35  // FromNillable is a function to make an Option type value with the nillable value with value de-referencing.
    36  // If the given value is not nil, this returns Some[T] value. On the other hand, if the value is nil, this returns None[T].
    37  // This function does "dereference" for the value on packing that into Option value. If this value is not preferable, please consider using PtrFromNillable() instead.
    38  func FromNillable[T any](v *T) Option[T] {
    39  	if v == nil {
    40  		return None[T]()
    41  	}
    42  	return Some[T](*v)
    43  }
    44  
    45  // PtrFromNillable is a function to make an Option type value with the nillable value without value de-referencing.
    46  // If the given value is not nil, this returns Some[*T] value. On the other hand, if the value is nil, this returns None[*T].
    47  // This function doesn't "dereference" the value on packing that into the Option value; in other words, this puts the as-is pointer value into the Option envelope.
    48  // This behavior contrasts with the FromNillable() function's one.
    49  func PtrFromNillable[T any](v *T) Option[*T] {
    50  	if v == nil {
    51  		return None[*T]()
    52  	}
    53  	return Some[*T](v)
    54  }
    55  
    56  // IsNone returns whether the Option *doesn't* have a value or not.
    57  func (o Option[T]) IsNone() bool {
    58  	return o == nil
    59  }
    60  
    61  // IsSome returns whether the Option has a value or not.
    62  func (o Option[T]) IsSome() bool {
    63  	return o != nil
    64  }
    65  
    66  // Unwrap returns the value regardless of Some/None status.
    67  // If the Option value is Some, this method returns the actual value.
    68  // On the other hand, if the Option value is None, this method returns the *default* value according to the type.
    69  func (o Option[T]) Unwrap() T {
    70  	if o.IsNone() {
    71  		var defaultValue T
    72  		return defaultValue
    73  	}
    74  	return o[value]
    75  }
    76  
    77  // UnwrapAsPtr returns the contained value in receiver Option as a pointer.
    78  // This is similar to `Unwrap()` method but the difference is this method returns a pointer value instead of the actual value.
    79  // If the receiver Option value is None, this method returns nil.
    80  func (o Option[T]) UnwrapAsPtr() *T {
    81  	if o.IsNone() {
    82  		return nil
    83  	}
    84  	return &o[value]
    85  }
    86  
    87  // Take takes the contained value in Option.
    88  // If Option value is Some, this returns the value that is contained in Option.
    89  // On the other hand, this returns an ErrNoneValueTaken as the second return value.
    90  func (o Option[T]) Take() (T, error) {
    91  	if o.IsNone() {
    92  		var defaultValue T
    93  		return defaultValue, ErrNoneValueTaken
    94  	}
    95  	return o[value], nil
    96  }
    97  
    98  // TakeOr returns the actual value if the Option has a value.
    99  // On the other hand, this returns fallbackValue.
   100  func (o Option[T]) TakeOr(fallbackValue T) T {
   101  	if o.IsNone() {
   102  		return fallbackValue
   103  	}
   104  	return o[value]
   105  }
   106  
   107  // TakeOrElse returns the actual value if the Option has a value.
   108  // On the other hand, this executes fallbackFunc and returns the result value of that function.
   109  func (o Option[T]) TakeOrElse(fallbackFunc func() T) T {
   110  	if o.IsNone() {
   111  		return fallbackFunc()
   112  	}
   113  	return o[value]
   114  }
   115  
   116  // Or returns the Option value according to the actual value existence.
   117  // If the receiver's Option value is Some, this function pass-through that to return. Otherwise, this value returns the `fallbackOptionValue`.
   118  func (o Option[T]) Or(fallbackOptionValue Option[T]) Option[T] {
   119  	if o.IsNone() {
   120  		return fallbackOptionValue
   121  	}
   122  	return o
   123  }
   124  
   125  // OrElse returns the Option value according to the actual value existence.
   126  // If the receiver's Option value is Some, this function pass-through that to return. Otherwise, this executes `fallbackOptionFunc` and returns the result value of that function.
   127  func (o Option[T]) OrElse(fallbackOptionFunc func() Option[T]) Option[T] {
   128  	if o.IsNone() {
   129  		return fallbackOptionFunc()
   130  	}
   131  	return o
   132  }
   133  
   134  // Filter returns self if the Option has a value and the value matches the condition of the predicate function.
   135  // In other cases (i.e. it doesn't match with the predicate or the Option is None), this returns None value.
   136  func (o Option[T]) Filter(predicate func(v T) bool) Option[T] {
   137  	if o.IsNone() || !predicate(o[value]) {
   138  		return None[T]()
   139  	}
   140  	return o
   141  }
   142  
   143  // IfSome calls given function with the value of Option if the receiver value is Some.
   144  func (o Option[T]) IfSome(f func(v T)) {
   145  	if o.IsNone() {
   146  		return
   147  	}
   148  	f(o[value])
   149  }
   150  
   151  // IfSomeWithError calls given function with the value of Option if the receiver value is Some.
   152  // This method propagates the error of given function, and if the receiver value is None, this returns nil error.
   153  func (o Option[T]) IfSomeWithError(f func(v T) error) error {
   154  	if o.IsNone() {
   155  		return nil
   156  	}
   157  	return f(o[value])
   158  }
   159  
   160  // IfNone calls given function if the receiver value is None.
   161  func (o Option[T]) IfNone(f func()) {
   162  	if o.IsSome() {
   163  		return
   164  	}
   165  	f()
   166  }
   167  
   168  // IfNoneWithError calls given function if the receiver value is None.
   169  // This method propagates the error of given function, and if the receiver value is Some, this returns nil error.
   170  func (o Option[T]) IfNoneWithError(f func() error) error {
   171  	if o.IsSome() {
   172  		return nil
   173  	}
   174  	return f()
   175  }
   176  
   177  func (o Option[T]) String() string {
   178  	if o.IsNone() {
   179  		return "None[]"
   180  	}
   181  
   182  	v := o.Unwrap()
   183  	if stringer, ok := interface{}(v).(fmt.Stringer); ok {
   184  		return fmt.Sprintf("Some[%s]", stringer)
   185  	}
   186  	return fmt.Sprintf("Some[%v]", v)
   187  }
   188  
   189  // Map converts given Option value to another Option value according to the mapper function.
   190  // If given Option value is None, this also returns None.
   191  func Map[T, U any](option Option[T], mapper func(v T) U) Option[U] {
   192  	if option.IsNone() {
   193  		return None[U]()
   194  	}
   195  
   196  	return Some(mapper(option[value]))
   197  }
   198  
   199  // MapOr converts given Option value to another *actual* value according to the mapper function.
   200  // If given Option value is None, this returns fallbackValue.
   201  func MapOr[T, U any](option Option[T], fallbackValue U, mapper func(v T) U) U {
   202  	if option.IsNone() {
   203  		return fallbackValue
   204  	}
   205  	return mapper(option[value])
   206  }
   207  
   208  // MapWithError converts given Option value to another Option value according to the mapper function that has the ability to return the value with an error.
   209  // If given Option value is None, this returns (None, nil). Else if the mapper returns an error then this returns (None, error).
   210  // Unless of them, i.e. given Option value is Some and the mapper doesn't return the error, this returns (Some[U], nil).
   211  func MapWithError[T, U any](option Option[T], mapper func(v T) (U, error)) (Option[U], error) {
   212  	if option.IsNone() {
   213  		return None[U](), nil
   214  	}
   215  
   216  	u, err := mapper(option[value])
   217  	if err != nil {
   218  		return None[U](), err
   219  	}
   220  	return Some(u), nil
   221  }
   222  
   223  // MapOrWithError converts given Option value to another *actual* value according to the mapper function that has the ability to return the value with an error.
   224  // If given Option value is None, this returns (fallbackValue, nil). Else if the mapper returns an error then returns (_, error).
   225  // Unless of them, i.e. given Option value is Some and the mapper doesn't return the error, this returns (U, nil).
   226  func MapOrWithError[T, U any](option Option[T], fallbackValue U, mapper func(v T) (U, error)) (U, error) {
   227  	if option.IsNone() {
   228  		return fallbackValue, nil
   229  	}
   230  	return mapper(option[value])
   231  }
   232  
   233  // FlatMap converts give Option value to another Option value according to the mapper function.
   234  // The difference from the Map is the mapper function returns an Option value instead of the bare value.
   235  // If given Option value is None, this also returns None.
   236  func FlatMap[T, U any](option Option[T], mapper func(v T) Option[U]) Option[U] {
   237  	if option.IsNone() {
   238  		return None[U]()
   239  	}
   240  
   241  	return mapper(option[value])
   242  }
   243  
   244  // FlatMapOr converts given Option value to another *actual* value according to the mapper function.
   245  // The difference from the MapOr is the mapper function returns an Option value instead of the bare value.
   246  // If given Option value is None or mapper function returns None, this returns fallbackValue.
   247  func FlatMapOr[T, U any](option Option[T], fallbackValue U, mapper func(v T) Option[U]) U {
   248  	if option.IsNone() {
   249  		return fallbackValue
   250  	}
   251  
   252  	return (mapper(option[value])).TakeOr(fallbackValue)
   253  }
   254  
   255  // FlatMapWithError converts given Option value to another Option value according to the mapper function that has the ability to return the value with an error.
   256  // The difference from the MapWithError is the mapper function returns an Option value instead of the bare value.
   257  // If given Option value is None, this returns (None, nil). Else if the mapper returns an error then this returns (None, error).
   258  // Unless of them, i.e. given Option value is Some and the mapper doesn't return the error, this returns (Some[U], nil).
   259  func FlatMapWithError[T, U any](option Option[T], mapper func(v T) (Option[U], error)) (Option[U], error) {
   260  	if option.IsNone() {
   261  		return None[U](), nil
   262  	}
   263  
   264  	mapped, err := mapper(option[value])
   265  	if err != nil {
   266  		return None[U](), err
   267  	}
   268  	return mapped, nil
   269  }
   270  
   271  // FlatMapOrWithError converts given Option value to another *actual* value according to the mapper function that has the ability to return the value with an error.
   272  // The difference from the MapOrWithError is the mapper function returns an Option value instead of the bare value.
   273  // If given Option value is None, this returns (fallbackValue, nil). Else if the mapper returns an error then returns ($zero_value_of_type, error).
   274  // Unless of them, i.e. given Option value is Some and the mapper doesn't return the error, this returns (U, nil).
   275  func FlatMapOrWithError[T, U any](option Option[T], fallbackValue U, mapper func(v T) (Option[U], error)) (U, error) {
   276  	if option.IsNone() {
   277  		return fallbackValue, nil
   278  	}
   279  
   280  	maybe, err := mapper(option[value])
   281  	if err != nil {
   282  		var zeroValue U
   283  		return zeroValue, err
   284  	}
   285  
   286  	return maybe.TakeOr(fallbackValue), nil
   287  }
   288  
   289  // Pair is a data type that represents a tuple that has two elements.
   290  type Pair[T, U any] struct {
   291  	Value1 T
   292  	Value2 U
   293  }
   294  
   295  // Zip zips two Options into a Pair that has each Option's value.
   296  // If either one of the Options is None, this also returns None.
   297  func Zip[T, U any](opt1 Option[T], opt2 Option[U]) Option[Pair[T, U]] {
   298  	if opt1.IsSome() && opt2.IsSome() {
   299  		return Some(Pair[T, U]{
   300  			Value1: opt1[value],
   301  			Value2: opt2[value],
   302  		})
   303  	}
   304  
   305  	return None[Pair[T, U]]()
   306  }
   307  
   308  // ZipWith zips two Options into a typed value according to the zipper function.
   309  // If either one of the Options is None, this also returns None.
   310  func ZipWith[T, U, V any](opt1 Option[T], opt2 Option[U], zipper func(opt1 T, opt2 U) V) Option[V] {
   311  	if opt1.IsSome() && opt2.IsSome() {
   312  		return Some(zipper(opt1[value], opt2[value]))
   313  	}
   314  	return None[V]()
   315  }
   316  
   317  // Unzip extracts the values from a Pair and pack them into each Option value.
   318  // If the given zipped value is None, this returns None for all return values.
   319  func Unzip[T, U any](zipped Option[Pair[T, U]]) (Option[T], Option[U]) {
   320  	if zipped.IsNone() {
   321  		return None[T](), None[U]()
   322  	}
   323  
   324  	pair := zipped[value]
   325  	return Some(pair.Value1), Some(pair.Value2)
   326  }
   327  
   328  // UnzipWith extracts the values from the given value according to the unzipper function and pack the into each Option value.
   329  // If the given zipped value is None, this returns None for all return values.
   330  func UnzipWith[T, U, V any](zipped Option[V], unzipper func(zipped V) (T, U)) (Option[T], Option[U]) {
   331  	if zipped.IsNone() {
   332  		return None[T](), None[U]()
   333  	}
   334  
   335  	v1, v2 := unzipper(zipped[value])
   336  	return Some(v1), Some(v2)
   337  }
   338  
   339  var jsonNull = []byte("null")
   340  
   341  func (o Option[T]) MarshalJSON() ([]byte, error) {
   342  	if o.IsNone() {
   343  		return jsonNull, nil
   344  	}
   345  
   346  	marshal, err := json.Marshal(o.Unwrap())
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  	return marshal, nil
   351  }
   352  
   353  func (o *Option[T]) UnmarshalJSON(data []byte) error {
   354  	if len(data) <= 0 || bytes.Equal(data, jsonNull) {
   355  		*o = None[T]()
   356  		return nil
   357  	}
   358  
   359  	var v T
   360  	err := json.Unmarshal(data, &v)
   361  	if err != nil {
   362  		return err
   363  	}
   364  	*o = Some(v)
   365  
   366  	return nil
   367  }