-
Notifications
You must be signed in to change notification settings - Fork 53
/
errors.go
85 lines (75 loc) · 1.38 KB
/
errors.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
package util
import (
"errors"
"sort"
"sync"
)
// MultiErrGroup
// Best effort to run all tasks, then combines all errors
//
// Example:
//
// var eg util.MutliErrGroup{}
// tasks := []func() error{/* */}
//
// for _, task := range tasks {
// eg.Go(func() error {
// return task()
// }
// }
//
// eg.Wait()
//
// if err := eg.Error(); err != nil {
// // handle error
// }
type MultiErrGroup struct {
errMu sync.Mutex
errs []error
sync.WaitGroup
}
func (i *MultiErrGroup) Add(tasks int) {
i.WaitGroup.Add(tasks)
}
func (i *MultiErrGroup) Done() {
i.WaitGroup.Done()
}
func (i *MultiErrGroup) Wait() {
i.WaitGroup.Wait()
}
func (i *MultiErrGroup) addError(err error) {
i.errMu.Lock()
defer i.errMu.Unlock()
i.errs = append(i.errs, err)
}
func (i *MultiErrGroup) Errors() []error {
i.errMu.Lock()
defer i.errMu.Unlock()
return i.errs
}
func (i *MultiErrGroup) Error() error {
if len(i.errs) == 0 {
return nil
}
duped := map[string]struct{}{}
resErr := []error{}
for _, err := range i.errs {
if _, ok := duped[err.Error()]; !ok {
duped[err.Error()] = struct{}{}
resErr = append(resErr, err)
}
}
sort.Slice(resErr, func(i, j int) bool {
return resErr[i].Error() < resErr[j].Error()
})
return errors.Join(resErr...)
}
func (i *MultiErrGroup) Go(fn func() error) {
i.Add(1)
go func() {
defer i.Done()
if err := fn(); err != nil {
i.addError(err)
}
}()
}