-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherrors.go
408 lines (381 loc) · 11 KB
/
errors.go
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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// Package errors defines the error handling used by all software.
package errors
import (
"bytes"
"encoding"
"encoding/binary"
"fmt"
"log"
"runtime"
"strings"
)
// Error is the type that implements the error interface.
// It contains a number of fields, each of different type.
// An Error value may leave some values unset.
type Error struct {
// Op is the operation being performed, usually the name of the method
// being invoked (Get, Put, etc.). It should not contain an at sign @.
Op Op
// Kind is the class of error, such as permission failure,
// or "Other" if its class is unknown or irrelevant.
Kind Kind
// The underlying error that triggered this one, if any.
Err error
// Stack information; used only when the 'debug' build tag is set.
stack
}
func (e *Error) isZero() bool {
return e.Op == "" && e.Kind == 0 && e.Err == nil
}
var (
_ error = (*Error)(nil)
_ encoding.BinaryUnmarshaler = (*Error)(nil)
_ encoding.BinaryMarshaler = (*Error)(nil)
)
// Op describes an operation, usually as the package and method,
// such as "key/server.Lookup".
type Op string
// Separator is the string used to separate nested errors. By
// default, to make errors easier on the eye, nested errors are
// indented on a new line. A server may instead choose to keep each
// error on a single line by modifying the separator string, perhaps
// to ":: ".
var Separator = ":\n\t"
// Kind defines the kind of error this is, mostly for use by systems
// such as FUSE that must act differently depending on the error.
type Kind uint8
// Kinds of errors.
//
// The values of the error kinds are common between both
// clients and servers. Do not reorder this list or remove
// any items since that will change their values.
// New items must be added only to the end.
const (
Other Kind = iota // Unclassified error. This value is not printed in the error message.
ValidationFailed // Validation Failed.
NotFoundInDB
NotFoundInSolr
NoSponseredJobsFound
RedirectedWithCorrectSlug
JSONParserError
HTTP404
InvalidJobsPresent
)
func (k Kind) String() string {
switch k {
case Other:
return "other error"
case ValidationFailed:
return "validation failed"
case NotFoundInDB:
return "not found in database"
case NotFoundInSolr:
return "not found in solr"
case JSONParserError:
return "error while parsing JSON"
case HTTP404:
return "URL not found"
case InvalidJobsPresent:
return "some jobs are invalid"
}
return "unknown error kind"
}
// E builds an error value from its arguments.
// There must be at least one argument or E panics.
// The type of each argument determines its meaning.
// If more than one argument of a given type is presented,
// only the last one is recorded.
//
// The types are:
// errors.Op
// The operation being performed, usually the method
// being invoked (Get, Put, etc.).
// string
// Treated as an error message and assigned to the
// Err field after a call to errors.Str. To avoid a common
// class of misuse, if the string contains an @, it will be
// treated as a PathName or UserName, as appropriate. Use
// errors.Str explicitly to avoid this special-casing.
// errors.Kind
// The class of error, such as permission failure.
// error
// The underlying error that triggered this one.
//
// If the error is printed, only those items that have been
// set to non-zero values will appear in the result.
//
// If Kind is not specified or Other, we set it to the Kind of
// the underlying error.
//
func E(args ...interface{}) error {
if len(args) == 0 {
panic("call to errors.E with no arguments")
}
e := &Error{}
for _, arg := range args {
switch arg := arg.(type) {
case Op:
e.Op = arg
case string:
// Someone might accidentally call us with a user or path name
// that is not of the right type. Take care of that and log it.
if strings.Contains(arg, "@") {
_, file, line, _ := runtime.Caller(1)
log.Printf("errors.E: unqualified type for %q from %s:%d", arg, file, line)
continue
}
e.Err = Str(arg)
case Kind:
e.Kind = arg
case *Error:
// Make a copy
copy := *arg
e.Err = ©
case error:
e.Err = arg
default:
_, file, line, _ := runtime.Caller(1)
log.Printf("errors.E: bad call from %s:%d: %v", file, line, args)
return Errorf("unknown type %T, value %v in error call", arg, arg)
}
}
// Populate stack information (only in debug mode).
e.populateStack()
prev, ok := e.Err.(*Error)
if !ok {
return e
}
// The previous error was also one of ours. Suppress duplications
if prev.Kind == e.Kind {
prev.Kind = Other
}
// If this error has Kind unset or Other, pull up the inner one.
if e.Kind == Other {
e.Kind = prev.Kind
prev.Kind = Other
}
return e
}
// pad appends str to the buffer if the buffer already has some data.
func pad(b *bytes.Buffer, str string) {
if b.Len() == 0 {
return
}
b.WriteString(str)
}
func (e *Error) Error() string {
b := new(bytes.Buffer)
e.printStack(b)
if e.Op != "" {
pad(b, ": ")
b.WriteString(string(e.Op))
}
if e.Kind != 0 {
pad(b, ": ")
b.WriteString(e.Kind.String())
}
if e.Err != nil {
// Indent on new line if we are cascading non-empty errors.
if prevErr, ok := e.Err.(*Error); ok {
if !prevErr.isZero() {
pad(b, Separator)
b.WriteString(e.Err.Error())
}
} else {
pad(b, ": ")
b.WriteString(e.Err.Error())
}
}
if b.Len() == 0 {
return "no error"
}
return b.String()
}
// Str Recreate the errors.New functionality of the standard Go errors package
// so we can create simple text errors when needed.
// Str returns an error that formats as the given text. It is intended to
// be used as the error-typed argument to the E function.
func Str(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
// Errorf is equivalent to fmt.Errorf, but allows clients to import only this
// package for all error handling.
func Errorf(format string, args ...interface{}) error {
return &errorString{fmt.Sprintf(format, args...)}
}
// MarshalAppend marshals err into a byte slice. The result is appended to b,
// which may be nil.
// It returns the argument slice unchanged if the error is nil.
func (e *Error) MarshalAppend(b []byte) []byte {
if e == nil {
return b
}
b = appendString(b, string(e.Op))
var tmp [16]byte // For use by PutVarint.
N := binary.PutVarint(tmp[:], int64(e.Kind))
b = append(b, tmp[:N]...)
b = MarshalErrorAppend(e.Err, b)
return b
}
// MarshalBinary marshals its receiver into a byte slice, which it returns.
// It returns nil if the error is nil. The returned error is always nil.
func (e *Error) MarshalBinary() ([]byte, error) {
return e.MarshalAppend(nil), nil
}
// MarshalErrorAppend marshals an arbitrary error into a byte slice.
// The result is appended to b, which may be nil.
// It returns the argument slice unchanged if the error is nil.
// If the error is not an *Error, it just records the result of err.Error().
// Otherwise it encodes the full Error struct.
func MarshalErrorAppend(err error, b []byte) []byte {
if err == nil {
return b
}
if e, ok := err.(*Error); ok {
// This is an errors.Error. Mark it as such.
b = append(b, 'E')
return e.MarshalAppend(b)
}
// Ordinary error.
b = append(b, 'e')
b = appendString(b, err.Error())
return b
}
// MarshalError marshals an arbitrary error and returns the byte slice.
// If the error is nil, it returns nil.
// It returns the argument slice unchanged if the error is nil.
// If the error is not an *Error, it just records the result of err.Error().
// Otherwise it encodes the full Error struct.
func MarshalError(err error) []byte {
return MarshalErrorAppend(err, nil)
}
// UnmarshalBinary unmarshals the byte slice into the receiver, which must be non-nil.
// The returned error is always nil.
func (e *Error) UnmarshalBinary(b []byte) error {
if len(b) == 0 {
return nil
}
data, b := getBytes(b)
if data != nil {
e.Op = Op(data)
}
k, N := binary.Varint(b)
e.Kind = Kind(k)
b = b[N:]
e.Err = UnmarshalError(b)
return nil
}
// UnmarshalError unmarshals the byte slice into an error value.
// If the slice is nil or empty, it returns nil.
// Otherwise the byte slice must have been created by MarshalError or
// MarshalErrorAppend.
// If the encoded error was of type *Error, the returned error value
// will have that underlying type. Otherwise it will be just a simple
// value that implements the error interface.
func UnmarshalError(b []byte) error {
if len(b) == 0 {
return nil
}
code := b[0]
b = b[1:]
switch code {
case 'e':
// Plain error.
var data []byte
data, b = getBytes(b)
if len(b) != 0 {
log.Printf("Unmarshal error: trailing bytes")
}
return Str(string(data))
case 'E':
// Error value.
var err Error
_ = err.UnmarshalBinary(b)
return &err
default:
log.Printf("Unmarshal error: corrupt data %q", b)
return Str(string(b))
}
}
func appendString(b []byte, str string) []byte {
var tmp [16]byte // For use by PutUvarint.
N := binary.PutUvarint(tmp[:], uint64(len(str)))
b = append(b, tmp[:N]...)
b = append(b, str...)
return b
}
// getBytes unmarshals the byte slice at b (uvarint count followed by bytes)
// and returns the slice followed by the remaining bytes.
// If there is insufficient data, both return values will be nil.
func getBytes(b []byte) (data, remaining []byte) {
u, N := binary.Uvarint(b)
if len(b) < N+int(u) {
log.Printf("Unmarshal error: bad encoding")
return nil, nil
}
if N == 0 {
log.Printf("Unmarshal error: bad encoding")
return nil, b
}
return b[N : N+int(u)], b[N+int(u):]
}
// Match compares its two error arguments. It can be used to check
// for expected errors in tests. Both arguments must have underlying
// type *Error or Match will return false. Otherwise it returns true
// iff every non-zero element of the first error is equal to the
// corresponding element of the second.
// If the Err field is a *Error, Match recurs on that field;
// otherwise it compares the strings returned by the Error methods.
// Elements that are in the second argument but not present in
// the first are ignored.
//
// For example,
// Match(errors.E(upspin.UserName("joe@schmoe.com"), errors.Permission), err)
// tests whether err is an Error with Kind=Permission and User=joe@schmoe.com.
func Match(err1, err2 error) bool {
e1, ok := err1.(*Error)
if !ok {
return false
}
e2, ok := err2.(*Error)
if !ok {
return false
}
if e1.Op != "" && e2.Op != e1.Op {
return false
}
if e1.Kind != Other && e2.Kind != e1.Kind {
return false
}
if e1.Err != nil {
if _, ok := e1.Err.(*Error); ok {
return Match(e1.Err, e2.Err)
}
if e2.Err == nil || e2.Err.Error() != e1.Err.Error() {
return false
}
}
return true
}
// Is reports whether err is an *Error of the given Kind.
// If err is nil then Is returns false.
func Is(kind Kind, err error) bool {
e, ok := err.(*Error)
if !ok {
return false
}
if e.Kind != Other {
return e.Kind == kind
}
if e.Err != nil {
return Is(kind, e.Err)
}
return false
}