// Nullable is a generic type, which implements a field that can be one of three states:
//
// - field is not set in the request
// - field is explicitly set to `null` in the request
// - field is explicitly set to a valid value in the request
//
// Nullable is intended to be used with JSON marshalling and unmarshalling.
//
// Internal implementation details:
//
// - map[true]T means a value was provided
// - map[false]T means an explicit null was provided
// - nil or zero map means the field was not provided
//
// If the field is expected to be optional, add the `omitempty` JSON tags. Do NOT use `*Nullable`!
//
// Adapted from https://github.com/golang/go/issues/64515#issuecomment-1841057182
typeNullable[Tany] map[bool]T
// NewNullableWithValue is a convenience helper to allow constructing a `Nullable` with a given value, for instance to construct a field inside a struct, without introducing an intermediate variable
funcNewNullableWithValue[Tany](tT) Nullable[T] {
varnNullable[T]
n.Set(t)
returnn
}
// NewNullNullable is a convenience helper to allow constructing a `Nullable` with an explicit `null`, for instance to construct a field inside a struct, without introducing an intermediate variable
funcNewNullNullable[Tany]() Nullable[T] {
varnNullable[T]
n.SetNull()
returnn
}
// Get retrieves the underlying value, if present, and returns an error if the value was not present
func (tNullable[T]) Get() (T, error) {
varemptyT
ift.IsNull() {
returnempty, errors.New("value is null")
}
if!t.IsSpecified() {
returnempty, errors.New("value is not specified")
}
returnt[true], nil
}
// Dig retrieves the underlying value or returns empty value if not present or was `null`. Use Get to distinguish between these cases.
func (tNullable[T]) GetOrEmpty() T {
varemptyT
if!t.IsSpecified() ||t.IsNull() {
returnempty
}
returnt[true]
}
func (tNullable[T]) MustGet() T {
v, err:=t.Get()
iferr!=nil {
panic(err)
}
returnv
}
// Set sets the underlying value to a given value
func (t*Nullable[T]) Set(valueT) {
*t=map[bool]T{true: value}
}
// IsNull indicate whether the field was sent, and had a value of `null`
func (tNullable[T]) IsNull() bool {
_, foundNull:=t[false]
returnfoundNull
}
// SetNull indicate that the field was sent, and had a value of `null`
func (t*Nullable[T]) SetNull() {
varemptyT
*t=map[bool]T{false: empty}
}
// IsSpecified indicates whether the field was sent
func (tNullable[T]) IsSpecified() bool {
returnlen(t) !=0
}
// SetUnspecified indicate whether the field was sent