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 }