1package param
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6	shimjson "github.com/openai/openai-go/internal/encoding/json"
  7	"time"
  8)
  9
 10func NewOpt[T comparable](v T) Opt[T] {
 11	return Opt[T]{Value: v, status: included}
 12}
 13
 14// Null creates optional field with the JSON value "null".
 15//
 16// To set a struct to null, use [NullStruct].
 17func Null[T comparable]() Opt[T] { return Opt[T]{status: null} }
 18
 19type status int8
 20
 21const (
 22	omitted status = iota
 23	null
 24	included
 25)
 26
 27// Opt represents an optional parameter of type T. Use
 28// the [Opt.Valid] method to confirm.
 29type Opt[T comparable] struct {
 30	Value T
 31	// indicates whether the field should be omitted, null, or valid
 32	status status
 33	opt
 34}
 35
 36// Valid returns true if the value is not "null" or omitted.
 37//
 38// To check if explicitly null, use [Opt.Null].
 39func (o Opt[T]) Valid() bool {
 40	var empty Opt[T]
 41	return o.status == included || o != empty && o.status != null
 42}
 43
 44func (o Opt[T]) Or(v T) T {
 45	if o.Valid() {
 46		return o.Value
 47	}
 48	return v
 49}
 50
 51func (o Opt[T]) String() string {
 52	if o.null() {
 53		return "null"
 54	}
 55	if s, ok := any(o.Value).(fmt.Stringer); ok {
 56		return s.String()
 57	}
 58	return fmt.Sprintf("%v", o.Value)
 59}
 60
 61func (o Opt[T]) MarshalJSON() ([]byte, error) {
 62	if !o.Valid() {
 63		return []byte("null"), nil
 64	}
 65	return json.Marshal(o.Value)
 66}
 67
 68func (o *Opt[T]) UnmarshalJSON(data []byte) error {
 69	if string(data) == "null" {
 70		o.status = null
 71		return nil
 72	}
 73
 74	var value *T
 75	if err := json.Unmarshal(data, &value); err != nil {
 76		return err
 77	}
 78
 79	if value == nil {
 80		o.status = omitted
 81		return nil
 82	}
 83
 84	o.status = included
 85	o.Value = *value
 86	return nil
 87}
 88
 89// MarshalJSONWithTimeLayout is necessary to bypass the internal caching performed
 90// by [json.Marshal]. Prefer to use [Opt.MarshalJSON] instead.
 91//
 92// This function requires that the generic type parameter of [Opt] is not [time.Time].
 93func (o Opt[T]) MarshalJSONWithTimeLayout(format string) []byte {
 94	t, ok := any(o.Value).(time.Time)
 95	if !ok || o.null() {
 96		return nil
 97	}
 98
 99	b, err := json.Marshal(t.Format(shimjson.TimeLayout(format)))
100	if err != nil {
101		return nil
102	}
103	return b
104}
105
106func (o Opt[T]) null() bool   { return o.status == null }
107func (o Opt[T]) isZero() bool { return o == Opt[T]{} }
108
109// opt helps limit the [Optional] interface to only types in this package
110type opt struct{}
111
112func (opt) implOpt() {}
113
114// This interface is useful for internal purposes.
115type Optional interface {
116	Valid() bool
117	null() bool
118
119	isZero() bool
120	implOpt()
121}