error.go 11 KB

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