multierr.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package multierr provides a simple multiple-error type.
  4. // It was inspired by github.com/go-multierror/multierror.
  5. package multierr
  6. import (
  7. "errors"
  8. "slices"
  9. "strings"
  10. )
  11. // An Error represents multiple errors.
  12. type Error struct {
  13. errs []error
  14. }
  15. // Error implements the error interface.
  16. func (e Error) Error() string {
  17. s := new(strings.Builder)
  18. s.WriteString("multiple errors:")
  19. for _, err := range e.errs {
  20. s.WriteString("\n\t")
  21. s.WriteString(err.Error())
  22. }
  23. return s.String()
  24. }
  25. // Errors returns a slice containing all errors in e.
  26. func (e Error) Errors() []error {
  27. return slices.Clone(e.errs)
  28. }
  29. // Unwrap returns the underlying errors as-is.
  30. func (e Error) Unwrap() []error {
  31. // Do not clone since Unwrap requires callers to not mutate the slice.
  32. // See the documentation in the Go "errors" package.
  33. return e.errs
  34. }
  35. // New returns an error composed from errs.
  36. // Some errors in errs get special treatment:
  37. // - nil errors are discarded
  38. // - errors of type Error are expanded into the top level
  39. //
  40. // If the resulting slice has length 0, New returns nil.
  41. // If the resulting slice has length 1, New returns that error.
  42. // If the resulting slice has length > 1, New returns that slice as an Error.
  43. func New(errs ...error) error {
  44. // First count the number of errors to avoid allocating.
  45. var n int
  46. var errFirst error
  47. for _, e := range errs {
  48. switch e := e.(type) {
  49. case nil:
  50. continue
  51. case Error:
  52. n += len(e.errs)
  53. if errFirst == nil && len(e.errs) > 0 {
  54. errFirst = e.errs[0]
  55. }
  56. default:
  57. n++
  58. if errFirst == nil {
  59. errFirst = e
  60. }
  61. }
  62. }
  63. if n <= 1 {
  64. return errFirst // nil if n == 0
  65. }
  66. // More than one error, allocate slice and construct the multi-error.
  67. dst := make([]error, 0, n)
  68. for _, e := range errs {
  69. switch e := e.(type) {
  70. case nil:
  71. continue
  72. case Error:
  73. dst = append(dst, e.errs...)
  74. default:
  75. dst = append(dst, e)
  76. }
  77. }
  78. return Error{errs: dst}
  79. }
  80. // Is reports whether any error in e matches target.
  81. func (e Error) Is(target error) bool {
  82. for _, err := range e.errs {
  83. if errors.Is(err, target) {
  84. return true
  85. }
  86. }
  87. return false
  88. }
  89. // As finds the first error in e that matches target, and if any is found,
  90. // sets target to that error value and returns true. Otherwise, it returns false.
  91. func (e Error) As(target any) bool {
  92. for _, err := range e.errs {
  93. if ok := errors.As(err, target); ok {
  94. return true
  95. }
  96. }
  97. return false
  98. }
  99. // Range performs a pre-order, depth-first iteration of the error tree
  100. // by successively unwrapping all error values.
  101. // For each iteration it calls fn with the current error value and
  102. // stops iteration if it ever reports false.
  103. func Range(err error, fn func(error) bool) bool {
  104. if err == nil {
  105. return true
  106. }
  107. if !fn(err) {
  108. return false
  109. }
  110. switch err := err.(type) {
  111. case interface{ Unwrap() error }:
  112. if err := err.Unwrap(); err != nil {
  113. if !Range(err, fn) {
  114. return false
  115. }
  116. }
  117. case interface{ Unwrap() []error }:
  118. for _, err := range err.Unwrap() {
  119. if !Range(err, fn) {
  120. return false
  121. }
  122. }
  123. }
  124. return true
  125. }