Skip to content

Commit

Permalink
sdk: support for nullable model field types
Browse files Browse the repository at this point in the history
  • Loading branch information
manicminer committed Aug 12, 2024
1 parent 5ca6da0 commit 1907966
Showing 1 changed file with 103 additions and 0 deletions.
103 changes: 103 additions & 0 deletions sdk/nullable/nullable.go
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
}

0 comments on commit 1907966

Please sign in to comment.