1package param
2
3import (
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "strings"
8 "sync"
9 "time"
10
11 shimjson "github.com/openai/openai-go/internal/encoding/json"
12
13 "github.com/tidwall/sjson"
14)
15
16// This type will not be stable and shouldn't be relied upon
17type EncodedAsDate Opt[time.Time]
18
19func (m EncodedAsDate) MarshalJSON() ([]byte, error) {
20 underlying := Opt[time.Time](m)
21 bytes := underlying.MarshalJSONWithTimeLayout("2006-01-02")
22 if len(bytes) > 0 {
23 return bytes, nil
24 }
25 return underlying.MarshalJSON()
26}
27
28// This uses a shimmed 'encoding/json' from Go 1.24, to support the 'omitzero' tag
29func MarshalObject[T OverridableObject](f T, underlying any) ([]byte, error) {
30 if f.IsNull() {
31 return []byte("null"), nil
32 } else if extras := f.GetExtraFields(); extras != nil {
33 bytes, err := shimjson.Marshal(underlying)
34 if err != nil {
35 return nil, err
36 }
37 for k, v := range extras {
38 bytes, err = sjson.SetBytes(bytes, k, v)
39 if err != nil {
40 return nil, err
41 }
42 }
43 return bytes, nil
44 } else if ovr, ok := f.IsOverridden(); ok {
45 return shimjson.Marshal(ovr)
46 } else {
47 return shimjson.Marshal(underlying)
48 }
49}
50
51// This uses a shimmed 'encoding/json' from Go 1.24, to support the 'omitzero' tag
52func MarshalUnion[T any](variants ...any) ([]byte, error) {
53 nPresent := 0
54 presentIdx := -1
55 for i, variant := range variants {
56 if !IsOmitted(variant) {
57 nPresent++
58 presentIdx = i
59 }
60 }
61 if nPresent == 0 || presentIdx == -1 {
62 return []byte(`null`), nil
63 } else if nPresent > 1 {
64 return nil, &json.MarshalerError{
65 Type: typeFor[T](),
66 Err: fmt.Errorf("expected union to have only one present variant, got %d", nPresent),
67 }
68 }
69 return shimjson.Marshal(variants[presentIdx])
70}
71
72// shimmed from Go 1.23 "reflect" package
73func typeFor[T any]() reflect.Type {
74 var v T
75 if t := reflect.TypeOf(v); t != nil {
76 return t // optimize for T being a non-interface kind
77 }
78 return reflect.TypeOf((*T)(nil)).Elem() // only for an interface kind
79}
80
81var optStringType = typeFor[Opt[string]]()
82var optIntType = typeFor[Opt[int64]]()
83var optFloatType = typeFor[Opt[float64]]()
84var optBoolType = typeFor[Opt[bool]]()
85
86var OptionalPrimitiveTypes map[reflect.Type][]int
87
88// indexOfUnderlyingValueField must only be called at initialization time
89func indexOfUnderlyingValueField(t reflect.Type) []int {
90 field, ok := t.FieldByName("Value")
91 if !ok {
92 panic("unreachable: initialization issue, underlying value field not found")
93 }
94 return field.Index
95}
96
97func init() {
98 OptionalPrimitiveTypes = map[reflect.Type][]int{
99 optStringType: indexOfUnderlyingValueField(optStringType),
100 optIntType: indexOfUnderlyingValueField(optIntType),
101 optFloatType: indexOfUnderlyingValueField(optFloatType),
102 optBoolType: indexOfUnderlyingValueField(optBoolType),
103 }
104}
105
106var structFieldsCache sync.Map
107
108func structFields(t reflect.Type) (map[string][]int, error) {
109 if cached, ok := structFieldsCache.Load(t); ok {
110 return cached.(map[string][]int), nil
111 }
112 if t.Kind() != reflect.Struct {
113 return nil, fmt.Errorf("resp: expected struct but got %v of kind %v", t.String(), t.Kind().String())
114 }
115 structFields := map[string][]int{}
116 for i := 0; i < t.NumField(); i++ {
117 field := t.Field(i)
118 name := strings.Split(field.Tag.Get("json"), ",")[0]
119 if name == "" || name == "-" || field.Anonymous {
120 continue
121 }
122 structFields[name] = field.Index
123 }
124 return structFields, nil
125}