encoder.go

  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}