1package param
2
3import (
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "time"
8
9 shimjson "github.com/openai/openai-go/internal/encoding/json"
10
11 "github.com/tidwall/sjson"
12)
13
14// EncodedAsDate is not be stable and shouldn't be relied upon
15type EncodedAsDate Opt[time.Time]
16
17type forceOmit int
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// MarshalObject uses a shimmed 'encoding/json' from Go 1.24, to support the 'omitzero' tag
29//
30// Stability for the API of MarshalObject is not guaranteed.
31func MarshalObject[T ParamStruct](f T, underlying any) ([]byte, error) {
32 return MarshalWithExtras(f, underlying, f.extraFields())
33}
34
35// MarshalWithExtras is used to marshal a struct with additional properties.
36//
37// Stability for the API of MarshalWithExtras is not guaranteed.
38func MarshalWithExtras[T ParamStruct, R any](f T, underlying any, extras map[string]R) ([]byte, error) {
39 if f.null() {
40 return []byte("null"), nil
41 } else if len(extras) > 0 {
42 bytes, err := shimjson.Marshal(underlying)
43 if err != nil {
44 return nil, err
45 }
46 for k, v := range extras {
47 var a any = v
48 if a == Omit {
49 // Errors when handling ForceOmitted are ignored.
50 if b, e := sjson.DeleteBytes(bytes, k); e == nil {
51 bytes = b
52 }
53 continue
54 }
55 bytes, err = sjson.SetBytes(bytes, k, v)
56 if err != nil {
57 return nil, err
58 }
59 }
60 return bytes, nil
61 } else if ovr, ok := f.Overrides(); ok {
62 return shimjson.Marshal(ovr)
63 } else {
64 return shimjson.Marshal(underlying)
65 }
66}
67
68// MarshalUnion uses a shimmed 'encoding/json' from Go 1.24, to support the 'omitzero' tag
69//
70// Stability for the API of MarshalUnion is not guaranteed.
71func MarshalUnion[T ParamStruct](metadata T, variants ...any) ([]byte, error) {
72 nPresent := 0
73 presentIdx := -1
74 for i, variant := range variants {
75 if !IsOmitted(variant) {
76 nPresent++
77 presentIdx = i
78 }
79 }
80 if nPresent == 0 || presentIdx == -1 {
81 if ovr, ok := metadata.Overrides(); ok {
82 return shimjson.Marshal(ovr)
83 }
84 return []byte(`null`), nil
85 } else if nPresent > 1 {
86 return nil, &json.MarshalerError{
87 Type: typeFor[T](),
88 Err: fmt.Errorf("expected union to have only one present variant, got %d", nPresent),
89 }
90 }
91 return shimjson.Marshal(variants[presentIdx])
92}
93
94// typeFor is shimmed from Go 1.23 "reflect" package
95func typeFor[T any]() reflect.Type {
96 var v T
97 if t := reflect.TypeOf(v); t != nil {
98 return t // optimize for T being a non-interface kind
99 }
100 return reflect.TypeOf((*T)(nil)).Elem() // only for an interface kind
101}