-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
242 lines (216 loc) · 7.23 KB
/
errors.go
File metadata and controls
242 lines (216 loc) · 7.23 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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package purejson
import (
"errors"
"fmt"
"github.com/amikos-tech/pure-simdjson/internal/bootstrap"
"github.com/amikos-tech/pure-simdjson/internal/ffi"
)
var (
// ErrInvalidHandle reports that a parser, document, or element handle was not valid.
ErrInvalidHandle = errors.New("invalid handle")
// ErrClosed reports use of a parser, document, or document-tied
// element/iterator after the underlying parser/doc has been closed or
// released.
ErrClosed = errors.New("closed")
// ErrParserBusy reports that a parser still owns a live document.
ErrParserBusy = errors.New("parser busy")
// ErrNumberOutOfRange reports that a numeric conversion overflowed the target type.
ErrNumberOutOfRange = errors.New("number out of range")
// ErrPrecisionLoss reports that a numeric conversion would lose precision.
ErrPrecisionLoss = errors.New("precision loss")
// ErrCPUUnsupported reports that the loaded native library cannot run on the current CPU.
ErrCPUUnsupported = errors.New("cpu unsupported")
// ErrABIVersionMismatch reports that the Go wrapper and native library expose different ABI versions.
ErrABIVersionMismatch = errors.New("abi version mismatch")
// ErrPanic reports that the native library trapped a Rust panic at the FFI boundary.
ErrPanic = errors.New("panic")
// ErrCPPException reports that the native library trapped a C++ exception before it crossed the FFI boundary.
ErrCPPException = errors.New("cpp exception")
// ErrInvalidJSON reports invalid JSON input.
ErrInvalidJSON = errors.New("invalid json")
// ErrElementNotFound reports lookup of a missing element.
ErrElementNotFound = errors.New("element not found")
// ErrWrongType reports an accessor call on the wrong JSON value kind.
ErrWrongType = errors.New("wrong type")
// ErrNotImplemented reports an optional native diagnostic surface that is
// unavailable in the loaded artifact.
ErrNotImplemented = errors.New("not implemented")
// ErrDepthLimitExceeded reports JSON nesting deeper than the native parser
// or materializer depth contract can process.
ErrDepthLimitExceeded = errors.New("depth limit exceeded")
// ErrInternal reports native panics, internal failures, and any status code
// not mapped to a dedicated sentinel.
ErrInternal = errors.New("internal error")
)
// Bootstrap error sentinels are re-exported from internal/bootstrap. Pointer
// identity is preserved, so errors.Is(err, purejson.ErrChecksumMismatch) and
// errors.Is(err, bootstrap.ErrChecksumMismatch) both match. Canonical
// definitions live in internal/bootstrap/errors.go — never call errors.New
// for these here.
var (
ErrChecksumMismatch = bootstrap.ErrChecksumMismatch
ErrAllSourcesFailed = bootstrap.ErrAllSourcesFailed
ErrNoChecksum = bootstrap.ErrNoChecksum
)
var errLoadLibrary = errors.New("load library")
// Error carries native status details while still participating in Go's
// sentinel-error matching via Unwrap. Status details are exposed through
// accessor methods so callers cannot mutate them after construction.
type Error struct {
code int32
offset uint64
message string
err error
}
// Error formats the native status details as a human-readable message while
// preserving the wrapped sentinel error semantics.
func (e *Error) Error() string {
if e == nil {
return "<nil>"
}
label := "purejson error"
if e.err != nil {
label = e.err.Error()
}
switch {
case e.code != 0 && e.message != "" && hasOffset(e.offset):
return fmt.Sprintf("%s (code=%d, offset=%d): %s", label, e.code, e.offset, e.message)
case e.code != 0 && e.message != "":
return fmt.Sprintf("%s (code=%d): %s", label, e.code, e.message)
case e.code != 0 && hasOffset(e.offset):
return fmt.Sprintf("%s (code=%d, offset=%d)", label, e.code, e.offset)
case e.code != 0:
return fmt.Sprintf("%s (code=%d)", label, e.code)
case e.message != "":
return fmt.Sprintf("%s: %s", label, e.message)
default:
return label
}
}
// Code returns the native status code returned by the FFI call.
func (e *Error) Code() int32 {
if e == nil {
return 0
}
return e.code
}
// Offset returns the reported byte offset for parse errors. Zero means unknown.
func (e *Error) Offset() uint64 {
if e == nil {
return 0
}
return e.offset
}
// Message returns the native error message, when available.
func (e *Error) Message() string {
if e == nil {
return ""
}
return e.message
}
// Unwrap returns the sentinel error associated with the native status code so
// callers can use errors.Is with the exported package errors.
func (e *Error) Unwrap() error {
if e == nil {
return nil
}
return e.err
}
type nativeDetails struct {
message string
offset uint64
}
func wrapParserStatus(bindings *ffi.Bindings, parser ffi.ParserHandle, code int32) error {
if code == int32(ffi.OK) {
return nil
}
details := nativeDetails{}
if bindings != nil {
if message, rc := bindings.ParserLastError(parser); rc == int32(ffi.OK) && message != "" {
details.message = message
}
if offset, rc := bindings.ParserLastErrorOffset(parser); rc == int32(ffi.OK) {
details.offset = offset
}
}
return newError(code, details, sentinelForStatus(code))
}
func wrapStatus(code int32) error {
if code == int32(ffi.OK) {
return nil
}
return newError(code, nativeDetails{}, sentinelForStatus(code))
}
func wrapABIMismatch(expected, actual uint32, libraryPath string) error {
return newError(int32(ffi.ErrABIMismatch), nativeDetails{
message: fmt.Sprintf("expected ABI 0x%08x, got 0x%08x from %s", expected, actual, libraryPath),
offset: ffi.LastErrorOffsetUnknown,
}, ErrABIVersionMismatch)
}
func wrapLoadFailure(message string, err error) error {
loadErr := errLoadLibrary
if err != nil {
loadErr = fmt.Errorf("%w: %v", errLoadLibrary, err)
}
return newError(0, nativeDetails{
message: message,
offset: ffi.LastErrorOffsetUnknown,
}, loadErr)
}
func newError(code int32, details nativeDetails, err error) error {
if code == int32(ffi.OK) && err == nil && details.message == "" {
return nil
}
if details.message == "" && !hasOffset(details.offset) && err != nil && code == 0 {
return err
}
return &Error{
code: code,
offset: normalizeOffset(details.offset),
message: details.message,
err: err,
}
}
func sentinelForStatus(code int32) error {
switch ffi.ErrorCode(code) {
case ffi.ErrInvalidHandle:
return ErrInvalidHandle
case ffi.ErrParserBusy:
return ErrParserBusy
case ffi.ErrWrongType:
return ErrWrongType
case ffi.ErrElementNotFound:
return ErrElementNotFound
case ffi.ErrNotImplemented:
return ErrNotImplemented
case ffi.ErrDepthLimit:
return ErrDepthLimitExceeded
case ffi.ErrInvalidJSON:
return ErrInvalidJSON
case ffi.ErrNumberOutOfRange:
return ErrNumberOutOfRange
case ffi.ErrPrecisionLoss:
return ErrPrecisionLoss
case ffi.ErrCPUUnsupported:
return ErrCPUUnsupported
case ffi.ErrABIMismatch:
return ErrABIVersionMismatch
case ffi.ErrPanic:
return ErrPanic
case ffi.ErrCPPException:
return ErrCPPException
default:
// ErrInvalidArg and ErrBufferTooSmall indicate a bug in the Go wrapper,
// not user error; they intentionally map to ErrInternal.
return ErrInternal
}
}
func normalizeOffset(offset uint64) uint64 {
if !hasOffset(offset) {
return 0
}
return offset
}
func hasOffset(offset uint64) bool {
return offset != 0 && offset != ffi.LastErrorOffsetUnknown
}