errors.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package errors
  14. import (
  15. "errors"
  16. "fmt"
  17. )
  18. // Aggregate represents an object that contains multiple errors, but does not
  19. // necessarily have singular semantic meaning.
  20. type Aggregate interface {
  21. error
  22. Errors() []error
  23. }
  24. // NewAggregate converts a slice of errors into an Aggregate interface, which
  25. // is itself an implementation of the error interface. If the slice is empty,
  26. // this returns nil.
  27. func NewAggregate(errlist []error) Aggregate {
  28. if len(errlist) == 0 {
  29. return nil
  30. }
  31. return aggregate(errlist)
  32. }
  33. // This helper implements the error and Errors interfaces. Keeping it private
  34. // prevents people from making an aggregate of 0 errors, which is not
  35. // an error, but does satisfy the error interface.
  36. type aggregate []error
  37. // Error is part of the error interface.
  38. func (agg aggregate) Error() string {
  39. if len(agg) == 0 {
  40. // This should never happen, really.
  41. return ""
  42. }
  43. if len(agg) == 1 {
  44. return agg[0].Error()
  45. }
  46. result := fmt.Sprintf("[%s", agg[0].Error())
  47. for i := 1; i < len(agg); i++ {
  48. result += fmt.Sprintf(", %s", agg[i].Error())
  49. }
  50. result += "]"
  51. return result
  52. }
  53. // Errors is part of the Aggregate interface.
  54. func (agg aggregate) Errors() []error {
  55. return []error(agg)
  56. }
  57. // Matcher is used to match errors. Returns true if the error matches.
  58. type Matcher func(error) bool
  59. // FilterOut removes all errors that match any of the matchers from the input
  60. // error. If the input is a singular error, only that error is tested. If the
  61. // input implements the Aggregate interface, the list of errors will be
  62. // processed recursively.
  63. //
  64. // This can be used, for example, to remove known-OK errors (such as io.EOF or
  65. // os.PathNotFound) from a list of errors.
  66. func FilterOut(err error, fns ...Matcher) error {
  67. if err == nil {
  68. return nil
  69. }
  70. if agg, ok := err.(Aggregate); ok {
  71. return NewAggregate(filterErrors(agg.Errors(), fns...))
  72. }
  73. if !matchesError(err, fns...) {
  74. return err
  75. }
  76. return nil
  77. }
  78. // matchesError returns true if any Matcher returns true
  79. func matchesError(err error, fns ...Matcher) bool {
  80. for _, fn := range fns {
  81. if fn(err) {
  82. return true
  83. }
  84. }
  85. return false
  86. }
  87. // filterErrors returns any errors (or nested errors, if the list contains
  88. // nested Errors) for which all fns return false. If no errors
  89. // remain a nil list is returned. The resulting silec will have all
  90. // nested slices flattened as a side effect.
  91. func filterErrors(list []error, fns ...Matcher) []error {
  92. result := []error{}
  93. for _, err := range list {
  94. r := FilterOut(err, fns...)
  95. if r != nil {
  96. result = append(result, r)
  97. }
  98. }
  99. return result
  100. }
  101. // Flatten takes an Aggregate, which may hold other Aggregates in arbitrary
  102. // nesting, and flattens them all into a single Aggregate, recursively.
  103. func Flatten(agg Aggregate) Aggregate {
  104. result := []error{}
  105. if agg == nil {
  106. return nil
  107. }
  108. for _, err := range agg.Errors() {
  109. if a, ok := err.(Aggregate); ok {
  110. r := Flatten(a)
  111. if r != nil {
  112. result = append(result, r.Errors()...)
  113. }
  114. } else {
  115. if err != nil {
  116. result = append(result, err)
  117. }
  118. }
  119. }
  120. return NewAggregate(result)
  121. }
  122. // AggregateGoroutines runs the provided functions in parallel, stuffing all
  123. // non-nil errors into the returned Aggregate.
  124. // Returns nil if all the functions complete successfully.
  125. func AggregateGoroutines(funcs ...func() error) Aggregate {
  126. errChan := make(chan error, len(funcs))
  127. for _, f := range funcs {
  128. go func(f func() error) { errChan <- f() }(f)
  129. }
  130. errs := make([]error, 0)
  131. for i := 0; i < cap(errChan); i++ {
  132. if err := <-errChan; err != nil {
  133. errs = append(errs, err)
  134. }
  135. }
  136. return NewAggregate(errs)
  137. }
  138. // ErrPreconditionViolated is returned when the precondition is violated
  139. var ErrPreconditionViolated = errors.New("precondition is violated")