Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/go.uber.org/multierr/error.go
2872 views
1
// Copyright (c) 2017-2023 Uber Technologies, Inc.
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a copy
4
// of this software and associated documentation files (the "Software"), to deal
5
// in the Software without restriction, including without limitation the rights
6
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
// copies of the Software, and to permit persons to whom the Software is
8
// furnished to do so, subject to the following conditions:
9
//
10
// The above copyright notice and this permission notice shall be included in
11
// all copies or substantial portions of the Software.
12
//
13
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
// THE SOFTWARE.
20
21
// Package multierr allows combining one or more errors together.
22
//
23
// # Overview
24
//
25
// Errors can be combined with the use of the Combine function.
26
//
27
// multierr.Combine(
28
// reader.Close(),
29
// writer.Close(),
30
// conn.Close(),
31
// )
32
//
33
// If only two errors are being combined, the Append function may be used
34
// instead.
35
//
36
// err = multierr.Append(reader.Close(), writer.Close())
37
//
38
// The underlying list of errors for a returned error object may be retrieved
39
// with the Errors function.
40
//
41
// errors := multierr.Errors(err)
42
// if len(errors) > 0 {
43
// fmt.Println("The following errors occurred:", errors)
44
// }
45
//
46
// # Appending from a loop
47
//
48
// You sometimes need to append into an error from a loop.
49
//
50
// var err error
51
// for _, item := range items {
52
// err = multierr.Append(err, process(item))
53
// }
54
//
55
// Cases like this may require knowledge of whether an individual instance
56
// failed. This usually requires introduction of a new variable.
57
//
58
// var err error
59
// for _, item := range items {
60
// if perr := process(item); perr != nil {
61
// log.Warn("skipping item", item)
62
// err = multierr.Append(err, perr)
63
// }
64
// }
65
//
66
// multierr includes AppendInto to simplify cases like this.
67
//
68
// var err error
69
// for _, item := range items {
70
// if multierr.AppendInto(&err, process(item)) {
71
// log.Warn("skipping item", item)
72
// }
73
// }
74
//
75
// This will append the error into the err variable, and return true if that
76
// individual error was non-nil.
77
//
78
// See [AppendInto] for more information.
79
//
80
// # Deferred Functions
81
//
82
// Go makes it possible to modify the return value of a function in a defer
83
// block if the function was using named returns. This makes it possible to
84
// record resource cleanup failures from deferred blocks.
85
//
86
// func sendRequest(req Request) (err error) {
87
// conn, err := openConnection()
88
// if err != nil {
89
// return err
90
// }
91
// defer func() {
92
// err = multierr.Append(err, conn.Close())
93
// }()
94
// // ...
95
// }
96
//
97
// multierr provides the Invoker type and AppendInvoke function to make cases
98
// like the above simpler and obviate the need for a closure. The following is
99
// roughly equivalent to the example above.
100
//
101
// func sendRequest(req Request) (err error) {
102
// conn, err := openConnection()
103
// if err != nil {
104
// return err
105
// }
106
// defer multierr.AppendInvoke(&err, multierr.Close(conn))
107
// // ...
108
// }
109
//
110
// See [AppendInvoke] and [Invoker] for more information.
111
//
112
// NOTE: If you're modifying an error from inside a defer, you MUST use a named
113
// return value for that function.
114
//
115
// # Advanced Usage
116
//
117
// Errors returned by Combine and Append MAY implement the following
118
// interface.
119
//
120
// type errorGroup interface {
121
// // Returns a slice containing the underlying list of errors.
122
// //
123
// // This slice MUST NOT be modified by the caller.
124
// Errors() []error
125
// }
126
//
127
// Note that if you need access to list of errors behind a multierr error, you
128
// should prefer using the Errors function. That said, if you need cheap
129
// read-only access to the underlying errors slice, you can attempt to cast
130
// the error to this interface. You MUST handle the failure case gracefully
131
// because errors returned by Combine and Append are not guaranteed to
132
// implement this interface.
133
//
134
// var errors []error
135
// group, ok := err.(errorGroup)
136
// if ok {
137
// errors = group.Errors()
138
// } else {
139
// errors = []error{err}
140
// }
141
package multierr // import "go.uber.org/multierr"
142
143
import (
144
"bytes"
145
"errors"
146
"fmt"
147
"io"
148
"strings"
149
"sync"
150
"sync/atomic"
151
)
152
153
var (
154
// Separator for single-line error messages.
155
_singlelineSeparator = []byte("; ")
156
157
// Prefix for multi-line messages
158
_multilinePrefix = []byte("the following errors occurred:")
159
160
// Prefix for the first and following lines of an item in a list of
161
// multi-line error messages.
162
//
163
// For example, if a single item is:
164
//
165
// foo
166
// bar
167
//
168
// It will become,
169
//
170
// - foo
171
// bar
172
_multilineSeparator = []byte("\n - ")
173
_multilineIndent = []byte(" ")
174
)
175
176
// _bufferPool is a pool of bytes.Buffers.
177
var _bufferPool = sync.Pool{
178
New: func() interface{} {
179
return &bytes.Buffer{}
180
},
181
}
182
183
type errorGroup interface {
184
Errors() []error
185
}
186
187
// Errors returns a slice containing zero or more errors that the supplied
188
// error is composed of. If the error is nil, a nil slice is returned.
189
//
190
// err := multierr.Append(r.Close(), w.Close())
191
// errors := multierr.Errors(err)
192
//
193
// If the error is not composed of other errors, the returned slice contains
194
// just the error that was passed in.
195
//
196
// Callers of this function are free to modify the returned slice.
197
func Errors(err error) []error {
198
return extractErrors(err)
199
}
200
201
// multiError is an error that holds one or more errors.
202
//
203
// An instance of this is guaranteed to be non-empty and flattened. That is,
204
// none of the errors inside multiError are other multiErrors.
205
//
206
// multiError formats to a semi-colon delimited list of error messages with
207
// %v and with a more readable multi-line format with %+v.
208
type multiError struct {
209
copyNeeded atomic.Bool
210
errors []error
211
}
212
213
// Errors returns the list of underlying errors.
214
//
215
// This slice MUST NOT be modified.
216
func (merr *multiError) Errors() []error {
217
if merr == nil {
218
return nil
219
}
220
return merr.errors
221
}
222
223
func (merr *multiError) Error() string {
224
if merr == nil {
225
return ""
226
}
227
228
buff := _bufferPool.Get().(*bytes.Buffer)
229
buff.Reset()
230
231
merr.writeSingleline(buff)
232
233
result := buff.String()
234
_bufferPool.Put(buff)
235
return result
236
}
237
238
// Every compares every error in the given err against the given target error
239
// using [errors.Is], and returns true only if every comparison returned true.
240
func Every(err error, target error) bool {
241
for _, e := range extractErrors(err) {
242
if !errors.Is(e, target) {
243
return false
244
}
245
}
246
return true
247
}
248
249
func (merr *multiError) Format(f fmt.State, c rune) {
250
if c == 'v' && f.Flag('+') {
251
merr.writeMultiline(f)
252
} else {
253
merr.writeSingleline(f)
254
}
255
}
256
257
func (merr *multiError) writeSingleline(w io.Writer) {
258
first := true
259
for _, item := range merr.errors {
260
if first {
261
first = false
262
} else {
263
w.Write(_singlelineSeparator)
264
}
265
io.WriteString(w, item.Error())
266
}
267
}
268
269
func (merr *multiError) writeMultiline(w io.Writer) {
270
w.Write(_multilinePrefix)
271
for _, item := range merr.errors {
272
w.Write(_multilineSeparator)
273
writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
274
}
275
}
276
277
// Writes s to the writer with the given prefix added before each line after
278
// the first.
279
func writePrefixLine(w io.Writer, prefix []byte, s string) {
280
first := true
281
for len(s) > 0 {
282
if first {
283
first = false
284
} else {
285
w.Write(prefix)
286
}
287
288
idx := strings.IndexByte(s, '\n')
289
if idx < 0 {
290
idx = len(s) - 1
291
}
292
293
io.WriteString(w, s[:idx+1])
294
s = s[idx+1:]
295
}
296
}
297
298
type inspectResult struct {
299
// Number of top-level non-nil errors
300
Count int
301
302
// Total number of errors including multiErrors
303
Capacity int
304
305
// Index of the first non-nil error in the list. Value is meaningless if
306
// Count is zero.
307
FirstErrorIdx int
308
309
// Whether the list contains at least one multiError
310
ContainsMultiError bool
311
}
312
313
// Inspects the given slice of errors so that we can efficiently allocate
314
// space for it.
315
func inspect(errors []error) (res inspectResult) {
316
first := true
317
for i, err := range errors {
318
if err == nil {
319
continue
320
}
321
322
res.Count++
323
if first {
324
first = false
325
res.FirstErrorIdx = i
326
}
327
328
if merr, ok := err.(*multiError); ok {
329
res.Capacity += len(merr.errors)
330
res.ContainsMultiError = true
331
} else {
332
res.Capacity++
333
}
334
}
335
return
336
}
337
338
// fromSlice converts the given list of errors into a single error.
339
func fromSlice(errors []error) error {
340
// Don't pay to inspect small slices.
341
switch len(errors) {
342
case 0:
343
return nil
344
case 1:
345
return errors[0]
346
}
347
348
res := inspect(errors)
349
switch res.Count {
350
case 0:
351
return nil
352
case 1:
353
// only one non-nil entry
354
return errors[res.FirstErrorIdx]
355
case len(errors):
356
if !res.ContainsMultiError {
357
// Error list is flat. Make a copy of it
358
// Otherwise "errors" escapes to the heap
359
// unconditionally for all other cases.
360
// This lets us optimize for the "no errors" case.
361
out := append(([]error)(nil), errors...)
362
return &multiError{errors: out}
363
}
364
}
365
366
nonNilErrs := make([]error, 0, res.Capacity)
367
for _, err := range errors[res.FirstErrorIdx:] {
368
if err == nil {
369
continue
370
}
371
372
if nested, ok := err.(*multiError); ok {
373
nonNilErrs = append(nonNilErrs, nested.errors...)
374
} else {
375
nonNilErrs = append(nonNilErrs, err)
376
}
377
}
378
379
return &multiError{errors: nonNilErrs}
380
}
381
382
// Combine combines the passed errors into a single error.
383
//
384
// If zero arguments were passed or if all items are nil, a nil error is
385
// returned.
386
//
387
// Combine(nil, nil) // == nil
388
//
389
// If only a single error was passed, it is returned as-is.
390
//
391
// Combine(err) // == err
392
//
393
// Combine skips over nil arguments so this function may be used to combine
394
// together errors from operations that fail independently of each other.
395
//
396
// multierr.Combine(
397
// reader.Close(),
398
// writer.Close(),
399
// pipe.Close(),
400
// )
401
//
402
// If any of the passed errors is a multierr error, it will be flattened along
403
// with the other errors.
404
//
405
// multierr.Combine(multierr.Combine(err1, err2), err3)
406
// // is the same as
407
// multierr.Combine(err1, err2, err3)
408
//
409
// The returned error formats into a readable multi-line error message if
410
// formatted with %+v.
411
//
412
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
413
func Combine(errors ...error) error {
414
return fromSlice(errors)
415
}
416
417
// Append appends the given errors together. Either value may be nil.
418
//
419
// This function is a specialization of Combine for the common case where
420
// there are only two errors.
421
//
422
// err = multierr.Append(reader.Close(), writer.Close())
423
//
424
// The following pattern may also be used to record failure of deferred
425
// operations without losing information about the original error.
426
//
427
// func doSomething(..) (err error) {
428
// f := acquireResource()
429
// defer func() {
430
// err = multierr.Append(err, f.Close())
431
// }()
432
//
433
// Note that the variable MUST be a named return to append an error to it from
434
// the defer statement. See also [AppendInvoke].
435
func Append(left error, right error) error {
436
switch {
437
case left == nil:
438
return right
439
case right == nil:
440
return left
441
}
442
443
if _, ok := right.(*multiError); !ok {
444
if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
445
// Common case where the error on the left is constantly being
446
// appended to.
447
errs := append(l.errors, right)
448
return &multiError{errors: errs}
449
} else if !ok {
450
// Both errors are single errors.
451
return &multiError{errors: []error{left, right}}
452
}
453
}
454
455
// Either right or both, left and right, are multiErrors. Rely on usual
456
// expensive logic.
457
errors := [2]error{left, right}
458
return fromSlice(errors[0:])
459
}
460
461
// AppendInto appends an error into the destination of an error pointer and
462
// returns whether the error being appended was non-nil.
463
//
464
// var err error
465
// multierr.AppendInto(&err, r.Close())
466
// multierr.AppendInto(&err, w.Close())
467
//
468
// The above is equivalent to,
469
//
470
// err := multierr.Append(r.Close(), w.Close())
471
//
472
// As AppendInto reports whether the provided error was non-nil, it may be
473
// used to build a multierr error in a loop more ergonomically. For example:
474
//
475
// var err error
476
// for line := range lines {
477
// var item Item
478
// if multierr.AppendInto(&err, parse(line, &item)) {
479
// continue
480
// }
481
// items = append(items, item)
482
// }
483
//
484
// Compare this with a version that relies solely on Append:
485
//
486
// var err error
487
// for line := range lines {
488
// var item Item
489
// if parseErr := parse(line, &item); parseErr != nil {
490
// err = multierr.Append(err, parseErr)
491
// continue
492
// }
493
// items = append(items, item)
494
// }
495
func AppendInto(into *error, err error) (errored bool) {
496
if into == nil {
497
// We panic if 'into' is nil. This is not documented above
498
// because suggesting that the pointer must be non-nil may
499
// confuse users into thinking that the error that it points
500
// to must be non-nil.
501
panic("misuse of multierr.AppendInto: into pointer must not be nil")
502
}
503
504
if err == nil {
505
return false
506
}
507
*into = Append(*into, err)
508
return true
509
}
510
511
// Invoker is an operation that may fail with an error. Use it with
512
// AppendInvoke to append the result of calling the function into an error.
513
// This allows you to conveniently defer capture of failing operations.
514
//
515
// See also, [Close] and [Invoke].
516
type Invoker interface {
517
Invoke() error
518
}
519
520
// Invoke wraps a function which may fail with an error to match the Invoker
521
// interface. Use it to supply functions matching this signature to
522
// AppendInvoke.
523
//
524
// For example,
525
//
526
// func processReader(r io.Reader) (err error) {
527
// scanner := bufio.NewScanner(r)
528
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
529
// for scanner.Scan() {
530
// // ...
531
// }
532
// // ...
533
// }
534
//
535
// In this example, the following line will construct the Invoker right away,
536
// but defer the invocation of scanner.Err() until the function returns.
537
//
538
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
539
//
540
// Note that the error you're appending to from the defer statement MUST be a
541
// named return.
542
type Invoke func() error
543
544
// Invoke calls the supplied function and returns its result.
545
func (i Invoke) Invoke() error { return i() }
546
547
// Close builds an Invoker that closes the provided io.Closer. Use it with
548
// AppendInvoke to close io.Closers and append their results into an error.
549
//
550
// For example,
551
//
552
// func processFile(path string) (err error) {
553
// f, err := os.Open(path)
554
// if err != nil {
555
// return err
556
// }
557
// defer multierr.AppendInvoke(&err, multierr.Close(f))
558
// return processReader(f)
559
// }
560
//
561
// In this example, multierr.Close will construct the Invoker right away, but
562
// defer the invocation of f.Close until the function returns.
563
//
564
// defer multierr.AppendInvoke(&err, multierr.Close(f))
565
//
566
// Note that the error you're appending to from the defer statement MUST be a
567
// named return.
568
func Close(closer io.Closer) Invoker {
569
return Invoke(closer.Close)
570
}
571
572
// AppendInvoke appends the result of calling the given Invoker into the
573
// provided error pointer. Use it with named returns to safely defer
574
// invocation of fallible operations until a function returns, and capture the
575
// resulting errors.
576
//
577
// func doSomething(...) (err error) {
578
// // ...
579
// f, err := openFile(..)
580
// if err != nil {
581
// return err
582
// }
583
//
584
// // multierr will call f.Close() when this function returns and
585
// // if the operation fails, its append its error into the
586
// // returned error.
587
// defer multierr.AppendInvoke(&err, multierr.Close(f))
588
//
589
// scanner := bufio.NewScanner(f)
590
// // Similarly, this scheduled scanner.Err to be called and
591
// // inspected when the function returns and append its error
592
// // into the returned error.
593
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
594
//
595
// // ...
596
// }
597
//
598
// NOTE: If used with a defer, the error variable MUST be a named return.
599
//
600
// Without defer, AppendInvoke behaves exactly like AppendInto.
601
//
602
// err := // ...
603
// multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
604
//
605
// // ...is roughly equivalent to...
606
//
607
// err := // ...
608
// multierr.AppendInto(&err, foo())
609
//
610
// The advantage of the indirection introduced by Invoker is to make it easy
611
// to defer the invocation of a function. Without this indirection, the
612
// invoked function will be evaluated at the time of the defer block rather
613
// than when the function returns.
614
//
615
// // BAD: This is likely not what the caller intended. This will evaluate
616
// // foo() right away and append its result into the error when the
617
// // function returns.
618
// defer multierr.AppendInto(&err, foo())
619
//
620
// // GOOD: This will defer invocation of foo unutil the function returns.
621
// defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
622
//
623
// multierr provides a few Invoker implementations out of the box for
624
// convenience. See [Invoker] for more information.
625
func AppendInvoke(into *error, invoker Invoker) {
626
AppendInto(into, invoker.Invoke())
627
}
628
629
// AppendFunc is a shorthand for [AppendInvoke].
630
// It allows using function or method value directly
631
// without having to wrap it into an [Invoker] interface.
632
//
633
// func doSomething(...) (err error) {
634
// w, err := startWorker(...)
635
// if err != nil {
636
// return err
637
// }
638
//
639
// // multierr will call w.Stop() when this function returns and
640
// // if the operation fails, it appends its error into the
641
// // returned error.
642
// defer multierr.AppendFunc(&err, w.Stop)
643
// }
644
func AppendFunc(into *error, fn func() error) {
645
AppendInvoke(into, Invoke(fn))
646
}
647
648