Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / go.uber.org / multierr / error.go
1 // Copyright (c) 2017 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.Combine(reader.Close(), writer.Close())
37 //
38 // This makes it possible to record resource cleanup failures from deferred
39 // blocks with the help of named return values.
40 //
41 //      func sendRequest(req Request) (err error) {
42 //              conn, err := openConnection()
43 //              if err != nil {
44 //                      return err
45 //              }
46 //              defer func() {
47 //                      err = multierr.Append(err, conn.Close())
48 //              }()
49 //              // ...
50 //      }
51 //
52 // The underlying list of errors for a returned error object may be retrieved
53 // with the Errors function.
54 //
55 //      errors := multierr.Errors(err)
56 //      if len(errors) > 0 {
57 //              fmt.Println("The following errors occurred:")
58 //      }
59 //
60 // Advanced Usage
61 //
62 // Errors returned by Combine and Append MAY implement the following
63 // interface.
64 //
65 //      type errorGroup interface {
66 //              // Returns a slice containing the underlying list of errors.
67 //              //
68 //              // This slice MUST NOT be modified by the caller.
69 //              Errors() []error
70 //      }
71 //
72 // Note that if you need access to list of errors behind a multierr error, you
73 // should prefer using the Errors function. That said, if you need cheap
74 // read-only access to the underlying errors slice, you can attempt to cast
75 // the error to this interface. You MUST handle the failure case gracefully
76 // because errors returned by Combine and Append are not guaranteed to
77 // implement this interface.
78 //
79 //      var errors []error
80 //      group, ok := err.(errorGroup)
81 //      if ok {
82 //              errors = group.Errors()
83 //      } else {
84 //              errors = []error{err}
85 //      }
86 package multierr // import "go.uber.org/multierr"
87
88 import (
89         "bytes"
90         "fmt"
91         "io"
92         "strings"
93         "sync"
94
95         "go.uber.org/atomic"
96 )
97
98 var (
99         // Separator for single-line error messages.
100         _singlelineSeparator = []byte("; ")
101
102         _newline = []byte("\n")
103
104         // Prefix for multi-line messages
105         _multilinePrefix = []byte("the following errors occurred:")
106
107         // Prefix for the first and following lines of an item in a list of
108         // multi-line error messages.
109         //
110         // For example, if a single item is:
111         //
112         //      foo
113         //      bar
114         //
115         // It will become,
116         //
117         //       -  foo
118         //          bar
119         _multilineSeparator = []byte("\n -  ")
120         _multilineIndent    = []byte("    ")
121 )
122
123 // _bufferPool is a pool of bytes.Buffers.
124 var _bufferPool = sync.Pool{
125         New: func() interface{} {
126                 return &bytes.Buffer{}
127         },
128 }
129
130 type errorGroup interface {
131         Errors() []error
132 }
133
134 // Errors returns a slice containing zero or more errors that the supplied
135 // error is composed of. If the error is nil, the returned slice is empty.
136 //
137 //      err := multierr.Append(r.Close(), w.Close())
138 //      errors := multierr.Errors(err)
139 //
140 // If the error is not composed of other errors, the returned slice contains
141 // just the error that was passed in.
142 //
143 // Callers of this function are free to modify the returned slice.
144 func Errors(err error) []error {
145         if err == nil {
146                 return nil
147         }
148
149         // Note that we're casting to multiError, not errorGroup. Our contract is
150         // that returned errors MAY implement errorGroup. Errors, however, only
151         // has special behavior for multierr-specific error objects.
152         //
153         // This behavior can be expanded in the future but I think it's prudent to
154         // start with as little as possible in terms of contract and possibility
155         // of misuse.
156         eg, ok := err.(*multiError)
157         if !ok {
158                 return []error{err}
159         }
160
161         errors := eg.Errors()
162         result := make([]error, len(errors))
163         copy(result, errors)
164         return result
165 }
166
167 // multiError is an error that holds one or more errors.
168 //
169 // An instance of this is guaranteed to be non-empty and flattened. That is,
170 // none of the errors inside multiError are other multiErrors.
171 //
172 // multiError formats to a semi-colon delimited list of error messages with
173 // %v and with a more readable multi-line format with %+v.
174 type multiError struct {
175         copyNeeded atomic.Bool
176         errors     []error
177 }
178
179 var _ errorGroup = (*multiError)(nil)
180
181 // Errors returns the list of underlying errors.
182 //
183 // This slice MUST NOT be modified.
184 func (merr *multiError) Errors() []error {
185         if merr == nil {
186                 return nil
187         }
188         return merr.errors
189 }
190
191 func (merr *multiError) Error() string {
192         if merr == nil {
193                 return ""
194         }
195
196         buff := _bufferPool.Get().(*bytes.Buffer)
197         buff.Reset()
198
199         merr.writeSingleline(buff)
200
201         result := buff.String()
202         _bufferPool.Put(buff)
203         return result
204 }
205
206 func (merr *multiError) Format(f fmt.State, c rune) {
207         if c == 'v' && f.Flag('+') {
208                 merr.writeMultiline(f)
209         } else {
210                 merr.writeSingleline(f)
211         }
212 }
213
214 func (merr *multiError) writeSingleline(w io.Writer) {
215         first := true
216         for _, item := range merr.errors {
217                 if first {
218                         first = false
219                 } else {
220                         w.Write(_singlelineSeparator)
221                 }
222                 io.WriteString(w, item.Error())
223         }
224 }
225
226 func (merr *multiError) writeMultiline(w io.Writer) {
227         w.Write(_multilinePrefix)
228         for _, item := range merr.errors {
229                 w.Write(_multilineSeparator)
230                 writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
231         }
232 }
233
234 // Writes s to the writer with the given prefix added before each line after
235 // the first.
236 func writePrefixLine(w io.Writer, prefix []byte, s string) {
237         first := true
238         for len(s) > 0 {
239                 if first {
240                         first = false
241                 } else {
242                         w.Write(prefix)
243                 }
244
245                 idx := strings.IndexByte(s, '\n')
246                 if idx < 0 {
247                         idx = len(s) - 1
248                 }
249
250                 io.WriteString(w, s[:idx+1])
251                 s = s[idx+1:]
252         }
253 }
254
255 type inspectResult struct {
256         // Number of top-level non-nil errors
257         Count int
258
259         // Total number of errors including multiErrors
260         Capacity int
261
262         // Index of the first non-nil error in the list. Value is meaningless if
263         // Count is zero.
264         FirstErrorIdx int
265
266         // Whether the list contains at least one multiError
267         ContainsMultiError bool
268 }
269
270 // Inspects the given slice of errors so that we can efficiently allocate
271 // space for it.
272 func inspect(errors []error) (res inspectResult) {
273         first := true
274         for i, err := range errors {
275                 if err == nil {
276                         continue
277                 }
278
279                 res.Count++
280                 if first {
281                         first = false
282                         res.FirstErrorIdx = i
283                 }
284
285                 if merr, ok := err.(*multiError); ok {
286                         res.Capacity += len(merr.errors)
287                         res.ContainsMultiError = true
288                 } else {
289                         res.Capacity++
290                 }
291         }
292         return
293 }
294
295 // fromSlice converts the given list of errors into a single error.
296 func fromSlice(errors []error) error {
297         res := inspect(errors)
298         switch res.Count {
299         case 0:
300                 return nil
301         case 1:
302                 // only one non-nil entry
303                 return errors[res.FirstErrorIdx]
304         case len(errors):
305                 if !res.ContainsMultiError {
306                         // already flat
307                         return &multiError{errors: errors}
308                 }
309         }
310
311         nonNilErrs := make([]error, 0, res.Capacity)
312         for _, err := range errors[res.FirstErrorIdx:] {
313                 if err == nil {
314                         continue
315                 }
316
317                 if nested, ok := err.(*multiError); ok {
318                         nonNilErrs = append(nonNilErrs, nested.errors...)
319                 } else {
320                         nonNilErrs = append(nonNilErrs, err)
321                 }
322         }
323
324         return &multiError{errors: nonNilErrs}
325 }
326
327 // Combine combines the passed errors into a single error.
328 //
329 // If zero arguments were passed or if all items are nil, a nil error is
330 // returned.
331 //
332 //      Combine(nil, nil)  // == nil
333 //
334 // If only a single error was passed, it is returned as-is.
335 //
336 //      Combine(err)  // == err
337 //
338 // Combine skips over nil arguments so this function may be used to combine
339 // together errors from operations that fail independently of each other.
340 //
341 //      multierr.Combine(
342 //              reader.Close(),
343 //              writer.Close(),
344 //              pipe.Close(),
345 //      )
346 //
347 // If any of the passed errors is a multierr error, it will be flattened along
348 // with the other errors.
349 //
350 //      multierr.Combine(multierr.Combine(err1, err2), err3)
351 //      // is the same as
352 //      multierr.Combine(err1, err2, err3)
353 //
354 // The returned error formats into a readable multi-line error message if
355 // formatted with %+v.
356 //
357 //      fmt.Sprintf("%+v", multierr.Combine(err1, err2))
358 func Combine(errors ...error) error {
359         return fromSlice(errors)
360 }
361
362 // Append appends the given errors together. Either value may be nil.
363 //
364 // This function is a specialization of Combine for the common case where
365 // there are only two errors.
366 //
367 //      err = multierr.Append(reader.Close(), writer.Close())
368 //
369 // The following pattern may also be used to record failure of deferred
370 // operations without losing information about the original error.
371 //
372 //      func doSomething(..) (err error) {
373 //              f := acquireResource()
374 //              defer func() {
375 //                      err = multierr.Append(err, f.Close())
376 //              }()
377 func Append(left error, right error) error {
378         switch {
379         case left == nil:
380                 return right
381         case right == nil:
382                 return left
383         }
384
385         if _, ok := right.(*multiError); !ok {
386                 if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
387                         // Common case where the error on the left is constantly being
388                         // appended to.
389                         errs := append(l.errors, right)
390                         return &multiError{errors: errs}
391                 } else if !ok {
392                         // Both errors are single errors.
393                         return &multiError{errors: []error{left, right}}
394                 }
395         }
396
397         // Either right or both, left and right, are multiErrors. Rely on usual
398         // expensive logic.
399         errors := [2]error{left, right}
400         return fromSlice(errors[0:])
401 }