encoder.go

  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}