param.go

  1package param
  2
  3import (
  4	"encoding/json"
  5	"reflect"
  6)
  7
  8// NullObj is used to mark a struct as null.
  9// To send null to an [Opt] field use [NullOpt].
 10func NullObj[T NullableObject, PT Settable[T]]() T {
 11	var t T
 12	pt := PT(&t)
 13	pt.setMetadata(nil)
 14	return *pt
 15}
 16
 17// To override a specific field in a struct, use its [WithExtraFields] method.
 18func OverrideObj[T OverridableObject, PT Settable[T]](v any) T {
 19	var t T
 20	pt := PT(&t)
 21	pt.setMetadata(nil)
 22	return *pt
 23}
 24
 25// IsOmitted returns true if v is the zero value of its type.
 26//
 27// It indicates if a field with the `json:"...,omitzero"` tag will be omitted
 28// from serialization.
 29//
 30// If v is set explicitly to the JSON value "null", this function will return false.
 31// Therefore, when available, prefer using the [IsPresent] method to check whether
 32// a field is present.
 33//
 34// Generally, this function should only be used on structs, arrays, maps.
 35func IsOmitted(v any) bool {
 36	if v == nil {
 37		return false
 38	}
 39	if o, ok := v.(interface{ IsOmitted() bool }); ok {
 40		return o.IsOmitted()
 41	}
 42	return reflect.ValueOf(v).IsZero()
 43}
 44
 45type NullableObject = overridableStruct
 46type OverridableObject = overridableStruct
 47
 48type Settable[T overridableStruct] interface {
 49	setMetadata(any)
 50	*T
 51}
 52
 53type overridableStruct interface {
 54	IsNull() bool
 55	IsOverridden() (any, bool)
 56	GetExtraFields() map[string]any
 57}
 58
 59// APIObject should be embedded in api object fields, preferably using an alias to make private
 60type APIObject struct{ metadata }
 61
 62// APIUnion should be embedded in all api unions fields, preferably using an alias to make private
 63type APIUnion struct{ metadata }
 64
 65type metadata struct{ any }
 66type metadataNull struct{}
 67type metadataExtraFields map[string]any
 68
 69// IsNull returns true if the field is the explicit value `null`,
 70// prefer using [IsPresent] to check for presence, since it checks against null and omitted.
 71func (m metadata) IsNull() bool {
 72	if _, ok := m.any.(metadataNull); ok {
 73		return true
 74	}
 75
 76	if msg, ok := m.any.(json.RawMessage); ok {
 77		return string(msg) == "null"
 78	}
 79
 80	return false
 81}
 82
 83func (m metadata) IsOverridden() (any, bool) {
 84	if _, ok := m.any.(metadataExtraFields); ok {
 85		return nil, false
 86	}
 87	return m.any, m.any != nil
 88}
 89
 90func (m metadata) GetExtraFields() map[string]any {
 91	if extras, ok := m.any.(metadataExtraFields); ok {
 92		return extras
 93	}
 94	return nil
 95}
 96
 97func (m *metadata) WithExtraFields(fields map[string]any) {
 98	m.any = metadataExtraFields(fields)
 99}
100
101func (m *metadata) setMetadata(override any) {
102	if override == nil {
103		m.any = metadataNull{}
104		return
105	}
106	m.any = override
107}