-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinterfaces.go
More file actions
142 lines (133 loc) · 4.24 KB
/
Copy pathinterfaces.go
File metadata and controls
142 lines (133 loc) · 4.24 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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package jsonx
import (
"encoding"
"encoding/json"
"fmt"
"reflect"
"unsafe"
)
// Interface type descriptors captured once so plan-build lookups are pure
// reflect.Type comparisons. We speak encoding/json's interfaces verbatim so
// any type that already satisfies them (including json.RawMessage) works
// without changes.
var (
unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
marshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
)
// MarshalerError wraps errors returned from MarshalJSON / MarshalText so the
// origin type is preserved, mirroring encoding/json.MarshalerError.
type MarshalerError struct {
Type reflect.Type
Err error
sourceFunc string
}
func (e *MarshalerError) Error() string {
src := e.sourceFunc
if src == "" {
src = "MarshalJSON"
}
return fmt.Sprintf("jsonx: error calling %s for type %s: %v", src, e.Type, e.Err)
}
func (e *MarshalerError) Unwrap() error { return e.Err }
// buildUnmarshalerDecoder emits a decoder that scans a single JSON value and
// hands the raw bytes to (*T).UnmarshalJSON. Invoked when *T satisfies
// json.Unmarshaler. Null is passed through verbatim (matches stdlib, which
// lets the Unmarshaler decide); pointer-level null handling — "set the
// pointer to nil instead of calling UnmarshalJSON" — lives in buildPtrDecoder.
func buildUnmarshalerDecoder(t reflect.Type) typedDecodeFn {
tt := t
return func(d *decoder, p unsafe.Pointer) error {
d.skipWS()
if d.p >= len(d.data) {
return syntaxErr("unexpected end", d.p)
}
start := d.p
if err := d.skipValue(); err != nil {
return err
}
raw := d.data[start:d.p]
// Match encoding/json's contract: UnmarshalJSON implementations must
// copy data themselves if they retain it after returning. RawMessage
// already does that, so avoid an unconditional copy here.
u := reflect.NewAt(tt, p).Interface().(json.Unmarshaler)
return u.UnmarshalJSON(raw)
}
}
// buildTextUnmarshalerDecoder is the JSON-string → UnmarshalText path.
// Only accepts a JSON string (or null, which leaves the value zeroed).
func buildTextUnmarshalerDecoder(t reflect.Type) typedDecodeFn {
tt := t
return func(d *decoder, p unsafe.Pointer) error {
d.skipWS()
if d.p >= len(d.data) {
return syntaxErr("unexpected end", d.p)
}
if d.data[d.p] == 'n' {
return d.decodeNull()
}
if d.data[d.p] != '"' {
return &UnmarshalTypeError{Value: "non-string", Type: tt, Offset: int64(d.p)}
}
raw, err := d.decodeStringRaw()
if err != nil {
return err
}
u := reflect.NewAt(tt, p).Interface().(encoding.TextUnmarshaler)
return u.UnmarshalText(raw)
}
}
// buildMarshalerEncoder emits an encoder that calls MarshalJSON and appends
// the (validated) bytes to the output buffer. ptrRecv selects between value-
// and pointer-receiver dispatch.
func buildMarshalerEncoder(t reflect.Type, ptrRecv bool) typedEncodeFn {
tt := t
return func(e *encoder, p unsafe.Pointer) error {
var iv interface{}
if ptrRecv {
iv = reflect.NewAt(tt, p).Interface()
} else {
iv = reflect.NewAt(tt, p).Elem().Interface()
}
m, ok := iv.(json.Marshaler)
if !ok {
return &UnsupportedTypeError{Type: tt}
}
raw, err := m.MarshalJSON()
if err != nil {
return &MarshalerError{Type: tt, Err: err, sourceFunc: "MarshalJSON"}
}
if !Valid(raw) {
return &MarshalerError{
Type: tt,
Err: fmt.Errorf("MarshalJSON returned invalid JSON"),
sourceFunc: "MarshalJSON",
}
}
e.buf = appendCompactEscapedJSON(e.buf, raw)
return nil
}
}
// buildTextMarshalerEncoder emits a MarshalText → JSON-string encoder.
func buildTextMarshalerEncoder(t reflect.Type, ptrRecv bool) typedEncodeFn {
tt := t
return func(e *encoder, p unsafe.Pointer) error {
var iv interface{}
if ptrRecv {
iv = reflect.NewAt(tt, p).Interface()
} else {
iv = reflect.NewAt(tt, p).Elem().Interface()
}
m, ok := iv.(encoding.TextMarshaler)
if !ok {
return &UnsupportedTypeError{Type: tt}
}
text, err := m.MarshalText()
if err != nil {
return &MarshalerError{Type: tt, Err: err, sourceFunc: "MarshalText"}
}
e.writeString(b2sUnsafe(text))
return nil
}
}