param.go

  1package param
  2
  3import (
  4	"encoding/json"
  5	"github.com/openai/openai-go/internal/encoding/json/sentinel"
  6	"reflect"
  7)
  8
  9// NullStruct is used to set a struct to the JSON value null.
 10// Check for null structs with [IsNull].
 11//
 12// Only the first type parameter should be provided,
 13// the type PtrT will be inferred.
 14//
 15//	json.Marshal(param.NullStruct[MyStruct]()) -> 'null'
 16//
 17// To send null to an [Opt] field use [Null].
 18func NullStruct[T ParamStruct, PtrT InferPtr[T]]() T {
 19	var t T
 20	pt := PtrT(&t)
 21	pt.setMetadata(nil)
 22	return *pt
 23}
 24
 25// Override replaces the value of a struct with any type.
 26//
 27// Only the first type parameter should be provided,
 28// the type PtrT will be inferred.
 29//
 30// It's often useful for providing raw JSON
 31//
 32//	param.Override[MyStruct](json.RawMessage(`{"foo": "bar"}`))
 33//
 34// The public fields of the returned struct T will be unset.
 35//
 36// To override a specific field in a struct, use its [SetExtraFields] method.
 37func Override[T ParamStruct, PtrT InferPtr[T]](v any) T {
 38	var t T
 39	pt := PtrT(&t)
 40	pt.setMetadata(v)
 41	return *pt
 42}
 43
 44// IsOmitted returns true if v is the zero value of its type.
 45//
 46// If IsOmitted is true, and the field uses a `json:"...,omitzero"` tag,
 47// the field will be omitted from the request.
 48//
 49// If v is set explicitly to the JSON value "null", IsOmitted returns false.
 50func IsOmitted(v any) bool {
 51	if v == nil {
 52		return false
 53	}
 54	if o, ok := v.(Optional); ok {
 55		return o.isZero()
 56	}
 57	return reflect.ValueOf(v).IsZero()
 58}
 59
 60// IsNull returns true if v was set to the JSON value null.
 61//
 62// To set a param to null use [NullStruct], [Null], [NullMap], or [NullSlice]
 63// depending on the type of v.
 64//
 65// IsNull returns false if the value is omitted.
 66func IsNull[T any](v T) bool {
 67	if nullable, ok := any(v).(ParamNullable); ok {
 68		return nullable.null()
 69	}
 70
 71	switch reflect.TypeOf(v).Kind() {
 72	case reflect.Slice, reflect.Map:
 73		return sentinel.IsNull(v)
 74	}
 75
 76	return false
 77}
 78
 79// ParamNullable encapsulates all structs in parameters,
 80// and all [Opt] types in parameters.
 81type ParamNullable interface {
 82	null() bool
 83}
 84
 85// ParamStruct represents the set of all structs that are
 86// used in API parameters, by convention these usually end in
 87// "Params" or "Param".
 88type ParamStruct interface {
 89	Overrides() (any, bool)
 90	null() bool
 91	extraFields() map[string]any
 92}
 93
 94// This is an implementation detail and should never be explicitly set.
 95type InferPtr[T ParamStruct] interface {
 96	setMetadata(any)
 97	*T
 98}
 99
100// APIObject should be embedded in api object fields, preferably using an alias to make private
101type APIObject struct{ metadata }
102
103// APIUnion should be embedded in all api unions fields, preferably using an alias to make private
104type APIUnion struct{ metadata }
105
106// Overrides returns the value of the struct when it is created with
107// [Override], the second argument helps differentiate an explicit null.
108func (m metadata) Overrides() (any, bool) {
109	if _, ok := m.any.(metadataExtraFields); ok {
110		return nil, false
111	}
112	return m.any, m.any != nil
113}
114
115// ExtraFields returns the extra fields added to the JSON object.
116func (m metadata) ExtraFields() map[string]any {
117	if extras, ok := m.any.(metadataExtraFields); ok {
118		return extras
119	}
120	return nil
121}
122
123// Omit can be used with [metadata.SetExtraFields] to ensure that a
124// required field is omitted. This is useful as an escape hatch for
125// when a required is unwanted for some unexpected reason.
126const Omit forceOmit = -1
127
128// SetExtraFields adds extra fields to the JSON object.
129//
130// SetExtraFields will override any existing fields with the same key.
131// For security reasons, ensure this is only used with trusted input data.
132//
133// To intentionally omit a required field, use [Omit].
134//
135//	foo.SetExtraFields(map[string]any{"bar": Omit})
136//
137// If the struct already contains the field ExtraFields, then this
138// method will have no effect.
139func (m *metadata) SetExtraFields(extraFields map[string]any) {
140	m.any = metadataExtraFields(extraFields)
141}
142
143// extraFields aliases [metadata.ExtraFields] to avoid name collisions.
144func (m metadata) extraFields() map[string]any { return m.ExtraFields() }
145
146func (m metadata) null() bool {
147	if _, ok := m.any.(metadataNull); ok {
148		return true
149	}
150
151	if msg, ok := m.any.(json.RawMessage); ok {
152		return string(msg) == "null"
153	}
154
155	return false
156}
157
158type metadata struct{ any }
159type metadataNull struct{}
160type metadataExtraFields map[string]any
161
162func (m *metadata) setMetadata(override any) {
163	if override == nil {
164		m.any = metadataNull{}
165		return
166	}
167	m.any = override
168}