-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherrorlist.go
176 lines (158 loc) · 4.01 KB
/
errorlist.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
package errors
import "strings"
// ErrorList contains one or more errors.
// It's useful if you care about the errors from deferred calls
// or if you're processing several things
// and want to return more than just the first error encountered.
// The empty ErrorList is considered a pathological case
// and its behavior is undefined.
type ErrorList interface {
// Error is here to satisfy error and for callers who don't know this
// might be a list. It probably doesn't give ideal results.
Error() string
// First returns the first error in the list.
First() error
// Walk is how callers who know this might be a list (possibly of lists)
// print all the items. None of the errors passed to walkFn will be
// an ErrorList.
Walk(walkFn func(error))
}
type errorList struct {
a []error
}
// Firster is a subset of ErrorList used by functions that don't need the whole
// thing.
type Firster interface {
// This is always used in such a way that Error()string will also be
// present.
First() error
}
func (list *errorList) First() error {
// They're not supposed to do this, but let's be permissive.
if len(list.a) < 1 {
return nil
}
return list.a[0]
}
// First returns ErrorList.First() if err is a Firster and err otherwise.
func First(err error) error {
if list, ok := err.(Firster); ok {
return list.First()
}
return err
}
// Walker is a subset of ErrorList used by functions that don't need the whole
// thing.
type Walker interface {
// This is always used in such a way that Error()string will also be
// present.
Walk(func(error))
}
// Walk implements ErrorList.Walk.
func (list *errorList) Walk(walkFn func(error)) {
for _, e := range list.a {
if l, ok := e.(Walker); ok {
l.Walk(walkFn)
} else {
walkFn(e)
}
}
}
// Walk calls ErrorList.Walk(walkFn) if err is a Walker
// and walkFn(err) otherwise.
func Walk(err error, walkFn func(error)) {
if list, ok := err.(Walker); ok {
list.Walk(walkFn)
return
}
walkFn(err)
}
type walkEnded struct{}
func (_ walkEnded) Error() string {
return "walk ended"
}
// WalkN visits the first n entries in err. It uses Walk.
func WalkN(err error, n int, walkFn func(error)) {
fn := func(e error) {
walkFn(e)
n--
if n <= 0 {
panic(walkEnded{})
}
}
defer func() {
e := recover()
if _, ok := e.(walkEnded); !ok {
panic(e)
}
}()
Walk(err, fn)
}
// Error implements ErrorList.Error.
func (list *errorList) Error() string {
if len(list.a) == 1 {
return list.a[0].Error()
}
// oh what do we tell you, simple caller?
a := make([]string, 0, len(list.a))
list.Walk(func(err error) {
a = append(a, err.Error())
})
return strings.Join(a, "\n")
}
// Append creates an ErrorList.
// It is fine to call it with any mix of nil, error, and Walker arguments.
// It will return nil, the only error passed in, or an ErrorList as appropriate.
// If the first non-nil argument is an ErrorList returned by this function,
// it may be modified.
func Append(errs ...error) error {
// The common case is no errors and two arguments.
// The general loop below takes 6+ times as long to handle this.
if len(errs) == 2 && errs[0] == nil && errs[1] == nil {
return nil
}
var a []error
var list *errorList
for _, e := range errs {
if e == nil {
continue
}
if len(a) == 0 {
if l, ok := e.(*errorList); ok {
a = l.a
list = l
continue
}
}
if l, ok := e.(Walker); ok {
l.Walk(func(err error) {
a = append(a, err)
})
continue
}
a = append(a, e)
}
switch len(a) {
case 0:
return nil
case 1:
return a[0]
default:
if list != nil {
list.a = a
return list
}
return &errorList{a}
}
panic("unreached")
}
// AppendCall appends any error returned by the function to any existing
// errors. It is useful when returning an error from a deferred call.
//
// WARNING: WHEN DOING THAT, MAKE SURE YOU PASS A POINTER TO A NAMED RETURN
// VALUE, ELSE THE RESULT WILL BE SILENTLY DISCARDED.
//
// See silentlybroken_test.go for examples of the problem.
func AppendCall(errp *error, f func() error) {
*errp = Append(*errp, f())
}