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}