-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy patheris.go
292 lines (260 loc) · 7.93 KB
/
eris.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
// Package eris is an error handling library with readable stack traces and flexible formatting support.
package eris
import (
"fmt"
"io"
"reflect"
)
// New creates a new root error with a static message.
func New(msg string) error {
stack := callers(3) // callers(3) skips this method, stack.callers, and runtime.Callers
return &rootError{
global: stack.isGlobal(),
msg: msg,
stack: stack,
}
}
// Errorf creates a new root error with a formatted message.
func Errorf(format string, args ...interface{}) error {
stack := callers(3)
return &rootError{
global: stack.isGlobal(),
msg: fmt.Sprintf(format, args...),
stack: stack,
}
}
// Wrap adds additional context to all error types while maintaining the type of the original error.
//
// This method behaves differently for each error type. For root errors, the stack trace is reset to the current
// callers which ensures traces are correct when using global/sentinel error values. Wrapped error types are simply
// wrapped with the new context. For external types (i.e. something other than root or wrap errors), this method
// attempts to unwrap them while building a new error chain. If an external type does not implement the unwrap
// interface, it flattens the error and creates a new root error from it before wrapping with the additional
// context.
func Wrap(err error, msg string) error {
return wrap(err, fmt.Sprint(msg))
}
// Wrapf adds additional context to all error types while maintaining the type of the original error.
//
// This is a convenience method for wrapping errors with formatted messages and is otherwise the same as Wrap.
func Wrapf(err error, format string, args ...interface{}) error {
return wrap(err, fmt.Sprintf(format, args...))
}
func wrap(err error, msg string) error {
if err == nil {
return nil
}
// callers(4) skips runtime.Callers, stack.callers, this method, and Wrap(f)
stack := callers(4)
// caller(3) skips stack.caller, this method, and Wrap(f)
// caller(skip) has a slightly different meaning which is why it's not 4 as above
frame := caller(3)
switch e := err.(type) {
case *rootError:
if e.global {
// create a new root error for global values to make sure nothing interferes with the stack
err = &rootError{
global: e.global,
msg: e.msg,
stack: stack,
}
} else {
// insert the frame into the stack
e.stack.insertPC(*stack)
}
case *wrapError:
// insert the frame into the stack
if root, ok := Cause(err).(*rootError); ok {
root.stack.insertPC(*stack)
}
default:
// return a new root error that wraps the external error
return &rootError{
msg: msg,
ext: e,
stack: stack,
}
}
return &wrapError{
msg: msg,
err: err,
frame: frame,
}
}
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains an Unwrap method
// returning error. Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
u, ok := err.(interface {
Unwrap() error
})
if !ok {
return nil
}
return u.Unwrap()
}
// Is reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if it implements a method
// Is(error) bool such that Is(target) returns true.
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflect.TypeOf(target).Comparable()
for {
if isComparable && err == target {
return true
}
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
if err = Unwrap(err); err == nil {
return false
}
}
}
// As finds the first error in err's chain that matches target. If there's a match, it sets target to that error
// value and returns true. Otherwise, it returns false.
//
// The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.
//
// An error matches target if the error's concrete value is assignable to the value pointed to by target,
// or if the error has a method As(interface{}) bool such that As(target) returns true.
func As(err error, target interface{}) bool {
if target == nil || err == nil {
return false
}
val := reflect.ValueOf(target)
typ := val.Type()
// target must be a non-nil pointer
if typ.Kind() != reflect.Ptr || val.IsNil() {
return false
}
// *target must be interface or implement error
if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(reflect.TypeOf((*error)(nil)).Elem()) {
return false
}
for {
errType := reflect.TypeOf(err)
if errType != reflect.TypeOf(&wrapError{}) && errType != reflect.TypeOf(&rootError{}) && reflect.TypeOf(err).AssignableTo(typ.Elem()) {
val.Elem().Set(reflect.ValueOf(err))
return true
}
if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
return true
}
if err = Unwrap(err); err == nil {
return false
}
}
}
// Cause returns the root cause of the error, which is defined as the first error in the chain. The original
// error is returned if it does not implement `Unwrap() error` and nil is returned if the error is nil.
func Cause(err error) error {
for {
uerr := Unwrap(err)
if uerr == nil {
return err
}
err = uerr
}
}
// StackFrames returns the trace of an error in the form of a program counter slice.
// Use this method if you want to pass the eris stack trace to some other error tracing library.
func StackFrames(err error) []uintptr {
for err != nil {
switch err := err.(type) {
case *rootError:
return err.StackFrames()
case *wrapError:
return err.StackFrames()
default:
return []uintptr{}
}
}
return []uintptr{}
}
type rootError struct {
global bool // flag indicating whether the error was declared globally
msg string // root error message
ext error // error type for wrapping external errors
stack *stack // root error stack trace
}
func (e *rootError) Error() string {
return fmt.Sprint(e)
}
func (e *rootError) Format(s fmt.State, verb rune) {
printError(e, s, verb)
}
func (e *rootError) Is(target error) bool {
if err, ok := target.(*rootError); ok {
return e.msg == err.msg
}
return e.msg == target.Error()
}
func (e *rootError) As(target interface{}) bool {
t := reflect.Indirect(reflect.ValueOf(target)).Interface()
if err, ok := t.(*rootError); ok {
if e.msg == err.msg {
reflect.ValueOf(target).Elem().Set(reflect.ValueOf(err))
return true
}
}
return false
}
func (e *rootError) Unwrap() error {
return e.ext
}
// StackFrames returns the trace of a root error in the form of a program counter slice.
// This method is currently called by an external error tracing library (Sentry).
func (e *rootError) StackFrames() []uintptr {
return *e.stack
}
type wrapError struct {
msg string // wrap error message
err error // error type representing the next error in the chain
frame *frame // wrap error stack frame
}
func (e *wrapError) Error() string {
return fmt.Sprint(e)
}
func (e *wrapError) Format(s fmt.State, verb rune) {
printError(e, s, verb)
}
func (e *wrapError) Is(target error) bool {
if err, ok := target.(*wrapError); ok {
return e.msg == err.msg
}
return e.msg == target.Error()
}
func (e *wrapError) As(target interface{}) bool {
t := reflect.Indirect(reflect.ValueOf(target)).Interface()
if err, ok := t.(*wrapError); ok {
if e.msg == err.msg {
reflect.ValueOf(target).Elem().Set(reflect.ValueOf(err))
return true
}
}
return false
}
func (e *wrapError) Unwrap() error {
return e.err
}
// StackFrames returns the trace of a wrap error in the form of a program counter slice.
// This method is currently called by an external error tracing library (Sentry).
func (e *wrapError) StackFrames() []uintptr {
return []uintptr{e.frame.pc()}
}
func printError(err error, s fmt.State, verb rune) {
var withTrace bool
switch verb {
case 'v':
if s.Flag('+') {
withTrace = true
}
}
str := ToString(err, withTrace)
_, _ = io.WriteString(s, str)
}