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// 	}
86package multierr // import "go.uber.org/multierr"
87
88import (
89	"bytes"
90	"fmt"
91	"io"
92	"strings"
93	"sync"
94
95	"go.uber.org/atomic"
96)
97
98var (
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.
124var _bufferPool = sync.Pool{
125	New: func() interface{} {
126		return &bytes.Buffer{}
127	},
128}
129
130type 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.
144func 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.
174type multiError struct {
175	copyNeeded atomic.Bool
176	errors     []error
177}
178
179var _ errorGroup = (*multiError)(nil)
180
181// Errors returns the list of underlying errors.
182//
183// This slice MUST NOT be modified.
184func (merr *multiError) Errors() []error {
185	if merr == nil {
186		return nil
187	}
188	return merr.errors
189}
190
191func (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
206func (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
214func (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
226func (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.
236func 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
255type 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.
272func 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.
296func 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))
358func 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// 		}()
377func 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}
402