-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sdk: support for nullable model field types
- Loading branch information
1 parent
5ca6da0
commit 1907966
Showing
1 changed file
with
103 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package nullable | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
) | ||
|
||
type Type[T comparable] map[bool]T | ||
|
||
func Value[T comparable](t T) Type[T] { | ||
var n Type[T] | ||
n.Set(t) | ||
return n | ||
} | ||
|
||
func NoZero[T comparable](t T) Type[T] { | ||
var n Type[T] | ||
n.SetNoZero(t) | ||
return n | ||
} | ||
|
||
// Get retrieves the underlying value, if present, and returns nil if the value is null | ||
func (t Type[T]) Get() *T { | ||
var empty T | ||
if t.IsNull() { | ||
return nil | ||
} | ||
if t.IsSpecified() { | ||
ret := t[true] | ||
return &ret | ||
} | ||
return &empty | ||
} | ||
|
||
// GetOrZero retrieves the underlying value, if present, and returns the zero value if null | ||
func (t Type[T]) GetOrZero() T { | ||
var empty T | ||
val := t.Get() | ||
if val == nil { | ||
return empty | ||
} | ||
return *val | ||
} | ||
|
||
// IsNull indicate whether the field was sent, and had a value of `null` | ||
func (t Type[T]) IsNull() bool { | ||
_, foundNull := t[false] | ||
return foundNull | ||
} | ||
|
||
// IsSpecified indicates whether the field was sent | ||
func (t Type[T]) IsSpecified() bool { | ||
return len(t) != 0 | ||
} | ||
|
||
// Set sets the underlying value to a given value | ||
func (t *Type[T]) Set(value T) { | ||
*t = map[bool]T{true: value} | ||
} | ||
|
||
// SetNoZero sets the underlying value to a given value, whilst also nulling the field if it was set to | ||
// its zero value. This ensures that zero values are sent as null. | ||
func (t *Type[T]) SetNoZero(value T) { | ||
var empty T | ||
*t = map[bool]T{value != empty: value} | ||
} | ||
|
||
// SetNull indicate that the field was sent, and had a value of `null` | ||
func (t *Type[T]) SetNull() { | ||
var empty T | ||
*t = map[bool]T{false: empty} | ||
} | ||
|
||
// SetUnspecified indicate whether the field was sent | ||
func (t *Type[T]) SetUnspecified() { | ||
*t = map[bool]T{} | ||
} | ||
|
||
func (t Type[T]) MarshalJSON() ([]byte, error) { | ||
// note: if value was unspecified, and `omitempty` is set on the field tags, `json.Marshal` will omit this field | ||
// if value was specified, and `null`, marshal it | ||
if t.IsNull() { | ||
return []byte("null"), nil | ||
} | ||
// otherwise, we have a value, so marshal it | ||
return json.Marshal(t[true]) | ||
} | ||
|
||
func (t *Type[T]) UnmarshalJSON(data []byte) error { | ||
// note: if value is unspecified, UnmarshalJSON won't be called | ||
// if value is specified and `null` | ||
if bytes.Equal(data, []byte("null")) { | ||
t.SetNull() | ||
return nil | ||
} | ||
// otherwise, we have an actual value, so parse it | ||
var val T | ||
if err := json.Unmarshal(data, &val); err != nil { | ||
return err | ||
} | ||
t.Set(val) | ||
return nil | ||
} |