-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathwrapper.go
169 lines (149 loc) · 4.01 KB
/
wrapper.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
package errors
import (
"fmt"
)
// wrapper.go defines interface and util method for error wrapping,
// it also implements Wrapper interface in un exported wrappedError
// Wrapper is based on go 2 proposal, it only has an Unwrap method to returns the underlying error
type Wrapper interface {
Unwrap() error
}
// Message return the top level error message without traverse the error chain
// i.e. when Error() returns `invalid config: file a.json does not exist` Message() returns `invalid config`
type Messenger interface {
Message() string
}
// Tracer is error with stack trace
type Tracer interface {
Stack() Stack
}
// Wrap creates a wrappedError with stack and set its cause to err.
//
// If the error being wrapped is already a Tracer, Wrap will reuse its stack trace instead of creating a new one.
// The error being wrapped has deeper stack than where the Wrap function is called and is closer to the root of error.
// This is based on https://github.com/pkg/errors/pull/122 to avoid having extra interface like WithMessage and WithStack
// like https://github.com/pkg/errors does.
//
// Wrap returns nil if the error you are trying to wrap is nil, thus if it is the last error checking in a func,
// you can return the wrap function directly in one line instead of using typical three line error check and wrap.
//
// return errors.Wrap(f.Close(), "failed to close file")
//
// if err := f.Close(); err != nil {
// return errors.Wrap(err, "failed to close file")
// }
// return nil。。
func Wrap(err error, msg string) error {
if err == nil {
return nil
}
// NOTE: keep the following in sync with Wrapf
if err == nil {
return nil
}
var stack *lazyStack
// reuse existing stack
if t, ok := err.(Tracer); ok {
stack = &lazyStack{
frames: t.Stack().Frames(),
}
} else {
stack = callers()
}
return &wrappedError{
msg: msg,
cause: err,
stack: stack,
}
}
// Wrapf is Wrap with fmt.Sprintf
func Wrapf(err error, format string, args ...interface{}) error {
// NOTE: copied from wrap instead of call Wrap due to caller
// -- copy & paste start
if err == nil {
return nil
}
var stack *lazyStack
// reuse existing stack
if t, ok := err.(Tracer); ok {
stack = &lazyStack{
frames: t.Stack().Frames(),
}
} else {
stack = callers()
}
// --- copy & paste end
return &wrappedError{
msg: fmt.Sprintf(format, args...),
cause: err,
stack: stack,
}
}
var (
_ error = (*freshError)(nil)
_ Tracer = (*freshError)(nil)
_ fmt.Formatter = (*freshError)(nil)
)
// freshError is a root error with stack trace
type freshError struct {
msg string
stack *lazyStack
}
func (fresh *freshError) Error() string {
return fresh.msg
}
func (fresh *freshError) Stack() Stack {
return fresh.stack
}
func (fresh *freshError) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
// TODO: print stack if s.Flag('+')
fallthrough
case 's':
Ignore2(s.Write([]byte(fresh.msg)))
case 'q':
// %q a double-quoted string safely escaped with Go syntax
Ignore2(fmt.Fprintf(s, "%q", fresh.msg))
}
}
var (
_ error = (*wrappedError)(nil)
_ Tracer = (*wrappedError)(nil)
_ causer = (*wrappedError)(nil)
_ Wrapper = (*wrappedError)(nil)
_ fmt.Formatter = (*wrappedError)(nil)
)
// wrappedError implements the Wrapper
type wrappedError struct {
msg string
cause error
stack *lazyStack
}
func (wrapped *wrappedError) Error() string {
return wrapped.msg + ErrCauseSep + wrapped.cause.Error()
}
func (wrapped *wrappedError) Stack() Stack {
return wrapped.stack
}
// Deprecated: use Unwrap
func (wrapped *wrappedError) Cause() error {
return wrapped.cause
}
func (wrapped *wrappedError) Unwrap() error {
return wrapped.cause
}
func (wrapped *wrappedError) Message() string {
return wrapped.msg
}
func (wrapped *wrappedError) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
// TODO: print stack if s.Flag('+')
fallthrough
case 's':
Ignore2(s.Write([]byte(wrapped.Error())))
case 'q':
Ignore2(fmt.Fprintf(s, "%q", wrapped.Error()))
}
}