openai/openai-go
Publicmirrored fromhttps://github.com/openai/openai-goAvailable
packages/param/param.go
186lines · modecode
| 1 | package param |
| 2 | |
| 3 | import ( |
| 4 | "encoding/json" |
| 5 | "github.com/openai/openai-go/v3/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]. |
| 18 | func 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. |
| 37 | func 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 | // SetJSON configures a param struct to serialize with the provided raw JSON data. |
| 45 | // Use this when you have existing JSON that you want to send as request parameters. |
| 46 | // |
| 47 | // var req example.NewUserParams |
| 48 | // var rawJSON = []byte(`{"name": "...", "age": 40}`) |
| 49 | // param.SetJSON(rawJSON, &req) |
| 50 | // res, err := client.Users.New(ctx, req) |
| 51 | // |
| 52 | // Note: The struct's existing fields will be ignored; only the provided JSON is serialized. |
| 53 | func SetJSON(rawJSON []byte, ptr anyParamStruct) { |
| 54 | ptr.setMetadata(json.RawMessage(rawJSON)) |
| 55 | } |
| 56 | |
| 57 | // IsOmitted returns true if v is the zero value of its type. |
| 58 | // |
| 59 | // If IsOmitted is true, and the field uses a `json:"...,omitzero"` tag, |
| 60 | // the field will be omitted from the request. |
| 61 | // |
| 62 | // If v is set explicitly to the JSON value "null", IsOmitted returns false. |
| 63 | func IsOmitted(v any) bool { |
| 64 | if v == nil { |
| 65 | return false |
| 66 | } |
| 67 | if o, ok := v.(Optional); ok { |
| 68 | return o.isZero() |
| 69 | } |
| 70 | return reflect.ValueOf(v).IsZero() |
| 71 | } |
| 72 | |
| 73 | // IsNull returns true if v was set to the JSON value null. |
| 74 | // |
| 75 | // To set a param to null use [NullStruct], [Null], [NullMap], or [NullSlice] |
| 76 | // depending on the type of v. |
| 77 | // |
| 78 | // IsNull returns false if the value is omitted. |
| 79 | func IsNull[T any](v T) bool { |
| 80 | if nullable, ok := any(v).(ParamNullable); ok { |
| 81 | return nullable.null() |
| 82 | } |
| 83 | |
| 84 | switch reflect.TypeOf(v).Kind() { |
| 85 | case reflect.Slice, reflect.Map: |
| 86 | return sentinel.IsNull(v) |
| 87 | } |
| 88 | |
| 89 | return false |
| 90 | } |
| 91 | |
| 92 | // ParamNullable encapsulates all structs in parameters, |
| 93 | // and all [Opt] types in parameters. |
| 94 | type ParamNullable interface { |
| 95 | null() bool |
| 96 | } |
| 97 | |
| 98 | // ParamStruct represents the set of all structs that are |
| 99 | // used in API parameters, by convention these usually end in |
| 100 | // "Params" or "Param". |
| 101 | type ParamStruct interface { |
| 102 | Overrides() (any, bool) |
| 103 | null() bool |
| 104 | extraFields() map[string]any |
| 105 | } |
| 106 | |
| 107 | // A pointer to ParamStruct |
| 108 | type anyParamStruct interface { |
| 109 | setMetadata(any) |
| 110 | } |
| 111 | |
| 112 | // This is an implementation detail and should never be explicitly set. |
| 113 | type InferPtr[T ParamStruct] interface { |
| 114 | setMetadata(any) |
| 115 | *T |
| 116 | } |
| 117 | |
| 118 | // APIObject should be embedded in api object fields, preferably using an alias to make private |
| 119 | type APIObject struct{ metadata } |
| 120 | |
| 121 | // APIUnion should be embedded in all api unions fields, preferably using an alias to make private |
| 122 | type APIUnion struct{ metadata } |
| 123 | |
| 124 | // Overrides returns the value of the struct when it is created with |
| 125 | // [Override], the second argument helps differentiate an explicit null. |
| 126 | func (m metadata) Overrides() (any, bool) { |
| 127 | if _, ok := m.any.(metadataExtraFields); ok { |
| 128 | return nil, false |
| 129 | } |
| 130 | return m.any, m.any != nil |
| 131 | } |
| 132 | |
| 133 | // ExtraFields returns the extra fields added to the JSON object. |
| 134 | func (m metadata) ExtraFields() map[string]any { |
| 135 | if extras, ok := m.any.(metadataExtraFields); ok { |
| 136 | return extras |
| 137 | } |
| 138 | return nil |
| 139 | } |
| 140 | |
| 141 | // Omit can be used with [metadata.SetExtraFields] to ensure that a |
| 142 | // required field is omitted. This is useful as an escape hatch for |
| 143 | // when a required is unwanted for some unexpected reason. |
| 144 | const Omit forceOmit = -1 |
| 145 | |
| 146 | // SetExtraFields adds extra fields to the JSON object. |
| 147 | // |
| 148 | // SetExtraFields will override any existing fields with the same key. |
| 149 | // For security reasons, ensure this is only used with trusted input data. |
| 150 | // |
| 151 | // To intentionally omit a required field, use [Omit]. |
| 152 | // |
| 153 | // foo.SetExtraFields(map[string]any{"bar": Omit}) |
| 154 | // |
| 155 | // If the struct already contains the field ExtraFields, then this |
| 156 | // method will have no effect. |
| 157 | func (m *metadata) SetExtraFields(extraFields map[string]any) { |
| 158 | m.any = metadataExtraFields(extraFields) |
| 159 | } |
| 160 | |
| 161 | // extraFields aliases [metadata.ExtraFields] to avoid name collisions. |
| 162 | func (m metadata) extraFields() map[string]any { return m.ExtraFields() } |
| 163 | |
| 164 | func (m metadata) null() bool { |
| 165 | if _, ok := m.any.(metadataNull); ok { |
| 166 | return true |
| 167 | } |
| 168 | |
| 169 | if msg, ok := m.any.(json.RawMessage); ok { |
| 170 | return string(msg) == "null" |
| 171 | } |
| 172 | |
| 173 | return false |
| 174 | } |
| 175 | |
| 176 | type metadata struct{ any } |
| 177 | type metadataNull struct{} |
| 178 | type metadataExtraFields map[string]any |
| 179 | |
| 180 | func (m *metadata) setMetadata(override any) { |
| 181 | if override == nil { |
| 182 | m.any = metadataNull{} |
| 183 | return |
| 184 | } |
| 185 | m.any = override |
| 186 | } |
| 187 | |