-
Notifications
You must be signed in to change notification settings - Fork 0
/
builder.go
177 lines (153 loc) · 5.46 KB
/
builder.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
package errors
import (
"fmt"
)
// Builder implementors can make and wrap errors.
type Builder interface {
Errorf(msg string, args ...interface{}) error
Wrap(err error, msg string, args ...interface{}) Wrapped
}
type builtinBuilder struct{}
// BuiltinBuilder has no frills. It is a proxy to built-in go packages.
var BuiltinBuilder Builder = (*builtinBuilder)(nil)
// Errorf is the same as fmt.Errorf
func (bb *builtinBuilder) Errorf(msg string, args ...interface{}) error {
return fmt.Errorf(msg, args...)
}
// Wrap is the same as errors.Wrap
func (bb *builtinBuilder) Wrap(
err error,
msg string,
args ...interface{},
) Wrapped {
return Wrap(err, msg, args...)
}
// signatured is an error that also has info about the function where it
// happened. It behaves like an error created with the builtin errors.New,
// except when processed with a function that is aware of its extra methods,
// like StackString.
type signatured struct {
// message describes the error
message string
// fi stores the location of the error
fi FuncInfo
// argStringer has a String() method that describes the arguments provided to
// the erroring function. The StackString function will add this between
// parenthesis after the function name.
argStringer interface{ String() string }
}
func (s *signatured) Error() string {
return s.message
}
// FuncInfo returns the location of the error.
func (s *signatured) FuncInfo() FuncInfo {
return s.fi
}
// ArgStringer returns a value with a String() method, which describes the
// arguments the erroring function was called with. This can be printed within
// parenthesis after the function name for debugging, as in StackString.
func (s *signatured) ArgStringer() interface{ String() string } {
return s.argStringer
}
// stringStringer has a String method that returns the underlying string value.
type stringStringer string
func (ss stringStringer) String() string {
return string(ss)
}
// formatStringer has a String method that calls fmt.Sprintf with predefined
// arguments.
type formatStringer struct {
fmt string
params []interface{}
}
func (fs formatStringer) String() string {
return fmt.Sprintf(fs.fmt, fs.params...)
}
// argsBuilder is the underlying type for NewBuilder.
type argsBuilder struct {
argString string
}
// NewBuilder returns an error builder that attaches info about the function
// where the error happened, and the args with which the function was called.
func NewBuilder(argFmt string, args ...interface{}) Builder {
ab := new(argsBuilder)
ab.argString = fmt.Sprintf(argFmt, args...)
return ab
}
// Errorf is the same as fmt.Errorf, except that the error message gets
// FuncInfo() and ArgStringer() methods, describing the context of the error.
func (ab *argsBuilder) Errorf(msg string, args ...interface{}) error {
return &signatured{
message: fmt.Sprintf(msg, args...),
fi: NewFuncInfo(1),
argStringer: stringStringer(ab.argString),
}
}
// Wrap replaces errors.Wrap, except that the error additionally implements the
// Wrapper() method, whose return value implements the FuncInfo() and
// ArgStringer() methods to describe the context of the error.
func (ab *argsBuilder) Wrap(
err error,
msg string,
args ...interface{},
) Wrapped {
return WrapWith(err, &signatured{
message: fmt.Sprintf(msg, args...),
fi: NewFuncInfo(1),
argStringer: stringStringer(ab.argString),
})
}
type lazyArgsBuilder struct {
argFmt string
args []interface{}
}
// NewLazyBuilder SHOULD NOT be used unless it is known that NewBuilder
// won't work. Frequent undisciplined usage of NewLazyBuilder can lead to
// poor code maintainability. It is similar to NewBuilder, except that the
// embedded ArgStringer()s do their formatting work lazily when the error is
// formatted for display, rather than up-front when the Builder is initialized.
// This can be important for performance in functions called thousands of times
// per second, but misleading debug messages can result if the arguments have
// changed since the function was first called. As a debug warning, any args
// are labeled "<lazy>" by the ArgStringer().
func NewLazyBuilder(argFmt string, args ...interface{}) Builder {
lab := new(lazyArgsBuilder)
lab.argFmt = argFmt
lab.args = args
return lab
}
// Errorf is the same as fmt.Errorf, except that the error message gets
// FuncInfo() and ArgStringer() methods, describing the context of the error.
// On lazy builders, ArgStringer() does its formatting computations when its
// String() method gets called.
func (lab *lazyArgsBuilder) Errorf(msg string, args ...interface{}) error {
argFmt := lab.argFmt
if len(argFmt) != 0 { // if no args, nothing to warn about
argFmt = "<lazy> " + argFmt
}
return &signatured{
message: fmt.Sprintf(msg, args...),
fi: NewFuncInfo(1),
argStringer: formatStringer{argFmt, lab.args},
}
}
// Wrap replaces errors.Wrap, except that the error additionally implements the
// Wrapper() method, whose return value implements the FuncInfo() and
// ArgStringer() methods to describe the context of the error.
// On lazy builders, ArgStringer() does its formatting computations when its
// String() method gets called.
func (lab *lazyArgsBuilder) Wrap(
err error,
msg string,
args ...interface{},
) Wrapped {
argFmt := lab.argFmt
if len(argFmt) != 0 { // if no args, nothing to warn about
argFmt = "<lazy> " + argFmt
}
return WrapWith(err, &signatured{
message: fmt.Sprintf(msg, args...),
fi: NewFuncInfo(1),
argStringer: formatStringer{argFmt, lab.args},
})
}