-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy patherrors.go
304 lines (264 loc) · 9.55 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
package merry
// The merry package augments standard golang errors with stacktraces
// and other context information.
//
// You can add any context information to an error with `e = merry.WithValue(e, "code", 12345)`
// You can retrieve that value with `v, _ := merry.Value(e, "code").(int)`
//
// Any error augmented like this will automatically get a stacktrace attached, if it doesn't have one
// already. If you just want to add the stacktrace, use `Wrap(e)`
//
// It also providers a way to override an error's message:
//
// var InvalidInputs = errors.New("Bad inputs")
//
// `Here()` captures a new stacktrace, and WithMessagef() sets a new error message:
//
// return merry.Here(InvalidInputs).WithMessagef("Bad inputs: %v", inputs)
//
// Errors are immutable. All functions and methods which add context return new errors.
// But errors can still be compared to the originals with `Is()`
//
// if merry.Is(err, InvalidInputs) {
//
// Functions which add context to errors have equivalent methods on *Error, to allow
// convenient chaining:
//
// return merry.New("Invalid body").WithHTTPCode(400)
//
// merry.Errors also implement fmt.Formatter, similar to github.com/pkg/errors.
//
// fmt.Sprintf("%+v", e) == merry.Details(e)
//
// pkg/errors Cause() interface is not implemented (yet).
import (
"errors"
"fmt"
v2 "github.com/ansel1/merry/v2"
)
// MaxStackDepth is no longer used. It remains here for backward compatibility.
// deprecated: See Set/GetMaxStackDepth.
var MaxStackDepth = 50
// StackCaptureEnabled returns whether stack capturing is enabled
func StackCaptureEnabled() bool {
return v2.StackCaptureEnabled()
}
// SetStackCaptureEnabled sets stack capturing globally. Disabling stack capture can increase performance
func SetStackCaptureEnabled(enabled bool) {
v2.SetStackCaptureEnabled(enabled)
}
// VerboseDefault no longer has any effect.
// deprecated: see SetVerboseDefault
func VerboseDefault() bool {
return false
}
// SetVerboseDefault used to control the behavior of the Error() function on errors
// processed by this package. Error() now always just returns the error's message.
// This setting no longer has any effect.
// deprecated: To print the details of an error, use Details(err), or format the
// error with the verbose flag: fmt.Sprintf("%+v", err)
func SetVerboseDefault(bool) {
}
// GetMaxStackDepth returns the number of frames captured in stacks.
func GetMaxStackDepth() int {
return v2.MaxStackDepth()
}
// SetMaxStackDepth sets the MaxStackDepth.
func SetMaxStackDepth(depth int) {
v2.SetMaxStackDepth(depth)
}
// New creates a new error, with a stack attached. The equivalent of golang's errors.New().
// Accepts v2 wrappers to apply to the error.
func New(msg string, wrappers ...v2.Wrapper) Error {
return WrapSkipping(errors.New(msg), 1, wrappers...)
}
// Errorf creates a new error with a formatted message and a stack. The equivalent of golang's fmt.Errorf().
// args can be format args, or v2 wrappers which will be applied to the error.
func Errorf(format string, args ...interface{}) Error {
var wrappers []v2.Wrapper
// pull out the args which are wrappers
n := 0
for _, arg := range args {
if w, ok := arg.(v2.Wrapper); ok {
wrappers = append(wrappers, w)
} else {
args[n] = arg
n++
}
}
args = args[:n]
return WrapSkipping(fmt.Errorf(format, args...), 1, wrappers...)
}
// UserError creates a new error with a message intended for display to an
// end user.
func UserError(msg string) Error {
return WrapSkipping(errors.New(msg), 1, v2.WithUserMessage(msg))
}
// UserErrorf is like UserError, but uses fmt.Sprintf()
func UserErrorf(format string, args ...interface{}) Error {
msg := fmt.Sprintf(format, args...)
return WrapSkipping(errors.New(msg), 1, v2.WithUserMessagef(msg))
}
// Wrap turns the argument into a merry.Error. If the argument already is a
// merry.Error, this is a no-op.
// If e == nil, return nil
func Wrap(err error, wrappers ...v2.Wrapper) Error {
return coerce(v2.WrapSkipping(err, 1, wrappers...))
}
// WrapSkipping turns the error arg into a merry.Error if the arg is not
// already a merry.Error.
// If e is nil, return nil.
// If a merry.Error is created by this call, the stack captured will skip
// `skip` frames (0 is the call site of `WrapSkipping()`)
func WrapSkipping(err error, skip int, wrappers ...v2.Wrapper) Error {
return coerce(v2.WrapSkipping(err, skip+1, wrappers...))
}
// WithValue adds a context an error. If the key was already set on e,
// the new value will take precedence.
// If e is nil, returns nil.
func WithValue(err error, key, value interface{}) Error {
return WrapSkipping(err, 1, v2.WithValue(key, value))
}
// Value returns the value for key, or nil if not set.
// If e is nil, returns nil.
func Value(err error, key interface{}) interface{} {
return v2.Value(err, key)
}
// Values returns a map of all values attached to the error
// If a key has been attached multiple times, the map will
// contain the last value mapped
// If e is nil, returns nil.
func Values(err error) map[interface{}]interface{} {
return v2.Values(err)
}
// RegisteredDetails extracts details registered with RegisterDetailFunc from an error, and
// returns them as a map. Values may be nil.
//
// If err is nil or there are no registered details, nil is returned.
func RegisteredDetails(err error) map[string]interface{} {
return v2.RegisteredDetails(err)
}
// Here returns an error with a new stacktrace, at the call site of Here().
// Useful when returning copies of exported package errors.
// If e is nil, returns nil.
func Here(err error) Error {
return WrapSkipping(err, 1, v2.CaptureStack(false))
}
// HereSkipping returns an error with a new stacktrace, at the call site
// of HereSkipping() - skip frames.
func HereSkipping(err error, skip int) Error {
return WrapSkipping(err, skip+1, v2.CaptureStack(false))
}
// Message returns just returns err.Error(). It is here for
// historical reasons.
func Message(err error) string {
if err == nil {
return ""
}
return err.Error()
}
// Stack returns the stack attached to an error, or nil if one is not attached
// If e is nil, returns nil.
func Stack(err error) []uintptr {
return v2.Stack(err)
}
// WithHTTPCode returns an error with an http code attached.
// If e is nil, returns nil.
func WithHTTPCode(e error, code int) Error {
return WrapSkipping(e, 1, v2.WithHTTPCode(code))
}
// HTTPCode converts an error to an http status code. All errors
// map to 500, unless the error has an http code attached.
// If e is nil, returns 200.
func HTTPCode(err error) int {
return v2.HTTPCode(err)
}
// UserMessage returns the end-user safe message. Returns empty if not set.
// If e is nil, returns "".
func UserMessage(err error) string {
return v2.UserMessage(err)
}
// Cause returns the cause of the argument. If e is nil, or has no cause,
// nil is returned.
func Cause(err error) error {
return v2.Cause(err)
}
// RootCause returns the innermost cause of the argument (i.e. the last
// error in the cause chain)
func RootCause(err error) error {
for {
cause := Cause(err)
if cause == nil {
return err
}
err = cause
}
}
// WithCause returns an error based on the first argument, with the cause
// set to the second argument. If e is nil, returns nil.
func WithCause(err error, cause error) Error {
return WrapSkipping(err, 1, v2.WithCause(cause))
}
// WithMessage returns an error with a new message.
// The resulting error's Error() method will return
// the new message.
// If e is nil, returns nil.
func WithMessage(err error, msg string) Error {
return WrapSkipping(err, 1, v2.WithMessage(msg))
}
// WithMessagef is the same as WithMessage(), using fmt.Sprintf().
func WithMessagef(err error, format string, args ...interface{}) Error {
return WrapSkipping(err, 1, v2.WithMessagef(format, args...))
}
// WithUserMessage adds a message which is suitable for end users to see.
// If e is nil, returns nil.
func WithUserMessage(err error, msg string) Error {
return WrapSkipping(err, 1, v2.WithUserMessage(msg))
}
// WithUserMessagef is the same as WithMessage(), using fmt.Sprintf()
func WithUserMessagef(err error, format string, args ...interface{}) Error {
return WrapSkipping(err, 1, v2.WithUserMessagef(format, args...))
}
// Append a message after the current error message, in the format "original: new".
// If e == nil, return nil.
func Append(err error, msg string) Error {
return WrapSkipping(err, 1, v2.AppendMessage(msg))
}
// Appendf is the same as Append, but uses fmt.Sprintf().
func Appendf(err error, format string, args ...interface{}) Error {
return WrapSkipping(err, 1, v2.AppendMessagef(format, args...))
}
// Prepend a message before the current error message, in the format "new: original".
// If e == nil, return nil.
func Prepend(err error, msg string) Error {
return WrapSkipping(err, 1, v2.PrependMessage(msg))
}
// Prependf is the same as Prepend, but uses fmt.Sprintf()
func Prependf(err error, format string, args ...interface{}) Error {
return WrapSkipping(err, 1, v2.PrependMessagef(format, args...))
}
// Is is equivalent to errors.Is, but tests against multiple targets.
//
// merry.Is(err1, err2, err3) == errors.Is(err1, err2) || errors.Is(err1, err3)
func Is(e error, originals ...error) bool {
for _, o := range originals {
if errors.Is(e, o) {
return true
}
}
return false
}
// Unwrap returns the innermost underlying error.
// This just calls errors.Unwrap() until if finds the deepest error.
// It isn't very useful, and only remains for historical purposes
//
// deprecated: use errors.Is() or errors.As() instead.
func Unwrap(e error) error {
for {
next := errors.Unwrap(e)
if next == nil {
return e
}
e = next
}
}