/
aggregate_forked.go
167 lines (149 loc) · 3.91 KB
/
aggregate_forked.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
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package errors
import (
"errors"
"sigs.k8s.io/kind/pkg/internal/sets"
)
/*
The contents of this file are lightly forked from k8s.io/apimachinery/pkg/util/errors
Forking makes kind easier to import, and this code is stable.
Currently the only source changes are renaming some methods so as to not
export them.
*/
// Aggregate represents an object that contains multiple errors, but does not
// necessarily have singular semantic meaning.
// The aggregate can be used with `errors.Is()` to check for the occurrence of
// a specific error type.
// Errors.As() is not supported, because the caller presumably cares about a
// specific error of potentially multiple that match the given type.
//
// NOTE: this type is originally from k8s.io/apimachinery/pkg/util/errors.Aggregate
// Since it is an interface, you can use the implementing types interchangeably
type Aggregate interface {
error
Errors() []error
Is(error) bool
}
func newAggregate(errlist []error) Aggregate {
if len(errlist) == 0 {
return nil
}
// In case of input error list contains nil
var errs []error
for _, e := range errlist {
if e != nil {
errs = append(errs, e)
}
}
if len(errs) == 0 {
return nil
}
return aggregate(errs)
}
// flatten takes an Aggregate, which may hold other Aggregates in arbitrary
// nesting, and flattens them all into a single Aggregate, recursively.
func flatten(agg Aggregate) Aggregate {
result := []error{}
if agg == nil {
return nil
}
for _, err := range agg.Errors() {
if a, ok := err.(Aggregate); ok {
r := flatten(a)
if r != nil {
result = append(result, r.Errors()...)
}
} else {
if err != nil {
result = append(result, err)
}
}
}
return newAggregate(result)
}
// reduce will return err or, if err is an Aggregate and only has one item,
// the first item in the aggregate.
func reduce(err error) error {
if agg, ok := err.(Aggregate); ok && err != nil {
switch len(agg.Errors()) {
case 1:
return agg.Errors()[0]
case 0:
return nil
}
}
return err
}
// This helper implements the error and Errors interfaces. Keeping it private
// prevents people from making an aggregate of 0 errors, which is not
// an error, but does satisfy the error interface.
type aggregate []error
// Error is part of the error interface.
func (agg aggregate) Error() string {
if len(agg) == 0 {
// This should never happen, really.
return ""
}
if len(agg) == 1 {
return agg[0].Error()
}
seenerrs := sets.NewString()
result := ""
agg.visit(func(err error) bool {
msg := err.Error()
if seenerrs.Has(msg) {
return false
}
seenerrs.Insert(msg)
if len(seenerrs) > 1 {
result += ", "
}
result += msg
return false
})
if len(seenerrs) == 1 {
return result
}
return "[" + result + "]"
}
func (agg aggregate) Is(target error) bool {
return agg.visit(func(err error) bool {
return errors.Is(err, target)
})
}
func (agg aggregate) visit(f func(err error) bool) bool {
for _, err := range agg {
switch err := err.(type) {
case aggregate:
if match := err.visit(f); match {
return match
}
case Aggregate:
for _, nestedErr := range err.Errors() {
if match := f(nestedErr); match {
return match
}
}
default:
if match := f(err); match {
return match
}
}
}
return false
}
// Errors is part of the Aggregate interface.
func (agg aggregate) Errors() []error {
return []error(agg)
}