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