github.com/mitranim/gg@v0.1.17/opt.go (about)

     1  package gg
     2  
     3  import "database/sql/driver"
     4  
     5  /*
     6  Short for "optional value". Instantiates an optional with the given val.
     7  The result is considered non-null.
     8  */
     9  func OptVal[A any](val A) Opt[A] { return Opt[A]{val, true} }
    10  
    11  /*
    12  Shortcut for creating an optional from a given value and boolean indicating
    13  validity. If the boolean is false, the output is considered "null" even if the
    14  value is not "zero".
    15  */
    16  func OptFrom[A any](val A, ok bool) Opt[A] { return Opt[A]{val, ok} }
    17  
    18  /*
    19  Short for "optional". Wraps an arbitrary type. When `.Ok` is false, the value is
    20  considered empty/null in various contexts such as text encoding, JSON encoding,
    21  SQL encoding, even if the value is not "zero".
    22  */
    23  type Opt[A any] struct {
    24  	Val A
    25  	Ok  bool
    26  }
    27  
    28  // Implement `Nullable`. True if not `.Ok`.
    29  func (self Opt[_]) IsNull() bool { return !self.Ok }
    30  
    31  // Inverse of `.IsNull`.
    32  func (self Opt[_]) IsNotNull() bool { return self.Ok }
    33  
    34  // Implement `Clearer`. Zeroes the receiver.
    35  func (self *Opt[_]) Clear() { PtrClear(self) }
    36  
    37  // Implement `Getter`, returning the underlying value as-is.
    38  func (self Opt[A]) Get() A { return self.Val }
    39  
    40  /*
    41  Implement `Setter`. Modifies the underlying value and sets `.Ok = true`.
    42  The resulting state is considered non-null even if the value is "zero".
    43  */
    44  func (self *Opt[A]) Set(val A) {
    45  	self.Val = val
    46  	self.Ok = true
    47  }
    48  
    49  // Implement `Ptrer`, returning a pointer to the underlying value.
    50  func (self *Opt[A]) Ptr() *A {
    51  	if self == nil {
    52  		return nil
    53  	}
    54  	return &self.Val
    55  }
    56  
    57  /*
    58  Implement `fmt.Stringer`. If `.IsNull`, returns an empty string. Otherwise uses
    59  the `String` function to encode the inner value.
    60  */
    61  func (self Opt[A]) String() string { return StringNull[A](self) }
    62  
    63  /*
    64  Implement `Parser`. If the input is empty, clears the receiver via `.Clear`.
    65  Otherwise uses the `ParseCatch` function, decoding into the underlying value.
    66  */
    67  func (self *Opt[A]) Parse(src string) error {
    68  	return self.with(ParseClearCatch[A](src, self))
    69  }
    70  
    71  // Implement `AppenderTo`, appending the same representation as `.String`.
    72  func (self Opt[A]) AppendTo(buf []byte) []byte { return AppendNull[A](buf, self) }
    73  
    74  // Implement `encoding.TextMarshaler`, returning the same representation as `.String`.
    75  func (self Opt[A]) MarshalText() ([]byte, error) { return MarshalNullCatch[A](self) }
    76  
    77  // Implement `encoding.TextUnmarshaler`, using the same logic as `.Parse`.
    78  func (self *Opt[A]) UnmarshalText(src []byte) error {
    79  	if len(src) <= 0 {
    80  		self.Clear()
    81  		return nil
    82  	}
    83  	return self.with(ParseClearCatch[A](src, self))
    84  }
    85  
    86  /*
    87  Implement `json.Marshaler`. If `.IsNull`, returns a representation of JSON null.
    88  Otherwise uses `json.Marshal` to encode the underlying value.
    89  */
    90  func (self Opt[A]) MarshalJSON() ([]byte, error) {
    91  	return JsonBytesNullCatch[A](self)
    92  }
    93  
    94  /*
    95  Implement `json.Unmarshaler`. If the input is empty or represents JSON null,
    96  clears the receiver via `.Clear`. Otherwise uses `JsonParseCatch` to decode
    97  into the underlying value.
    98  */
    99  func (self *Opt[A]) UnmarshalJSON(src []byte) error {
   100  	if IsJsonEmpty(src) {
   101  		self.Clear()
   102  		return nil
   103  	}
   104  	return self.with(JsonParseCatch(src, &self.Val))
   105  }
   106  
   107  /*
   108  Implement SQL `driver.Valuer`. If `.IsNull`, returns nil. If the underlying
   109  value implements `driver.Valuer`, delegates to its method. Otherwise returns
   110  the underlying value as-is.
   111  */
   112  func (self Opt[A]) Value() (driver.Value, error) { return ValueNull[A](self) }
   113  
   114  /*
   115  Implement SQL `Scanner`, decoding an arbitrary input into the underlying value.
   116  If the underlying type implements `Scanner`, delegates to that implementation.
   117  Otherwise input must be nil or text-like (see `Text`). Text decoding uses the
   118  same logic as `.Parse`.
   119  */
   120  func (self *Opt[A]) Scan(src any) error {
   121  	if src == nil {
   122  		self.Clear()
   123  		return nil
   124  	}
   125  
   126  	val, ok := src.(A)
   127  	if ok {
   128  		self.Set(val)
   129  		return nil
   130  	}
   131  
   132  	return self.with(ScanCatch[A](src, self))
   133  }
   134  
   135  func (self *Opt[_]) with(err error) error {
   136  	self.Ok = err == nil
   137  	return err
   138  }
   139  
   140  /*
   141  FP-style "mapping". If the original value is considered "null", or if the
   142  function is nil, the output is "zero" and "null". Otherwise the output is the
   143  result of calling the function with the previous value, and is considered
   144  non-"null" even if the value is zero.
   145  */
   146  func OptMap[A, B any](src Opt[A], fun func(A) B) (out Opt[B]) {
   147  	if src.IsNotNull() && fun != nil {
   148  		out.Set(fun(src.Val))
   149  	}
   150  	return
   151  }