2019-03-19 09:31:57 -04:00
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"bytes"
2020-10-30 07:54:36 -04:00
stderrors "errors"
2019-03-19 09:31:57 -04:00
"fmt"
2020-10-28 11:24:58 -04:00
"io"
2019-03-19 09:31:57 -04:00
)
2020-12-10 03:12:01 -05:00
// NilOrMultiError type allows combining multiple errors into one.
type NilOrMultiError struct {
errs [ ] error
}
2019-03-19 09:31:57 -04:00
2020-12-10 03:12:01 -05:00
// NewMulti returns NilOrMultiError with provided errors added if not nil.
func NewMulti ( errs ... error ) * NilOrMultiError {
m := & NilOrMultiError { }
2020-10-28 11:24:58 -04:00
m . Add ( errs ... )
return m
}
// Add adds single or many errors to the error list. Each error is added only if not nil.
2020-12-10 03:12:01 -05:00
// If the error is a multiError type, the errors inside multiError are added to the main NilOrMultiError.
func ( e * NilOrMultiError ) Add ( errs ... error ) {
2020-10-28 11:24:58 -04:00
for _ , err := range errs {
if err == nil {
continue
}
2020-10-30 07:54:36 -04:00
if merr , ok := err . ( multiError ) ; ok {
2020-12-10 03:12:01 -05:00
e . errs = append ( e . errs , merr . errs ... )
2020-10-28 11:24:58 -04:00
continue
}
2020-12-10 03:12:01 -05:00
e . errs = append ( e . errs , err )
2020-10-28 11:24:58 -04:00
}
}
2020-12-10 03:12:01 -05:00
// Err returns the error list as an Result (also implements error) or nil if it is empty.
func ( e * NilOrMultiError ) Err ( ) Result {
if len ( e . errs ) == 0 {
2020-10-28 11:24:58 -04:00
return nil
}
2020-12-10 03:12:01 -05:00
return multiError ( * e )
2020-10-30 07:54:36 -04:00
}
2020-12-10 03:12:01 -05:00
// Result is extended error interface that allows to use returned read-only multi error in more advanced ways.
type Result interface {
2020-10-30 07:54:36 -04:00
error
// Errors returns underlying errors.
Errors ( ) [ ] error
// As finds the first error in multiError slice of error chains that matches target, and if so, sets
// target to that error value and returns true. Otherwise, it returns false.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
As ( target interface { } ) bool
// Is returns true if any error in multiError's slice of error chains matches the given target or
// if the target is of multiError type.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
Is ( target error ) bool
// Count returns the number of multi error' errors that match the given target.
// Matching is defined as in Is method.
Count ( target error ) int
2020-10-28 11:24:58 -04:00
}
2020-12-10 03:12:01 -05:00
// multiError implements the error and Result interfaces, and it represents NilOrMultiError (in other words []error) with at least one error inside it.
// NOTE: This type is useful to make sure that NilOrMultiError is not accidentally used for err != nil check.
2020-10-30 07:54:36 -04:00
type multiError struct {
errs [ ] error
}
// Errors returns underlying errors.
func ( e multiError ) Errors ( ) [ ] error {
return e . errs
2020-10-28 11:24:58 -04:00
}
// Error returns a concatenated string of the contained errors.
2020-10-30 07:54:36 -04:00
func ( e multiError ) Error ( ) string {
2019-03-19 09:31:57 -04:00
var buf bytes . Buffer
2020-10-30 07:54:36 -04:00
if len ( e . errs ) > 1 {
fmt . Fprintf ( & buf , "%d errors: " , len ( e . errs ) )
2019-03-19 09:31:57 -04:00
}
2020-10-30 07:54:36 -04:00
for i , err := range e . errs {
2019-03-19 09:31:57 -04:00
if i != 0 {
buf . WriteString ( "; " )
}
buf . WriteString ( err . Error ( ) )
}
return buf . String ( )
}
2020-10-30 07:54:36 -04:00
// As finds the first error in multiError slice of error chains that matches target, and if so, sets
// target to that error value and returns true. Otherwise, it returns false.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
func ( e multiError ) As ( target interface { } ) bool {
if t , ok := target . ( * multiError ) ; ok {
* t = e
return true
}
for _ , err := range e . errs {
if stderrors . As ( err , target ) {
return true
}
}
return false
}
// Is returns true if any error in multiError's slice of error chains matches the given target or
// if the target is of multiError type.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
func ( e multiError ) Is ( target error ) bool {
if m , ok := target . ( multiError ) ; ok {
if len ( m . errs ) != len ( e . errs ) {
return false
}
for i := 0 ; i < len ( e . errs ) ; i ++ {
if ! stderrors . Is ( m . errs [ i ] , e . errs [ i ] ) {
return false
}
}
return true
}
for _ , err := range e . errs {
if stderrors . Is ( err , target ) {
return true
}
}
return false
}
// Count returns the number of all multi error' errors that match the given target (including nested multi errors).
// Matching is defined as in Is method.
func ( e multiError ) Count ( target error ) ( count int ) {
for _ , err := range e . errs {
if inner , ok := AsMulti ( err ) ; ok {
count += inner . Count ( target )
continue
}
if stderrors . Is ( err , target ) {
count ++
}
}
return count
}
2020-12-10 03:12:01 -05:00
// As casts error to multi error read only interface. It returns multi error and true if error matches multi error as
2020-10-30 07:54:36 -04:00
// defined by As method. If returns false if no multi error can be found.
2020-12-10 03:12:01 -05:00
func AsMulti ( err error ) ( Result , bool ) {
2020-10-30 07:54:36 -04:00
m := multiError { }
if ! stderrors . As ( err , & m ) {
return nil , false
}
return m , true
}
// CloseAll closes all given closers while recording error in multiError.
2020-10-28 11:24:58 -04:00
func CloseAll ( cs [ ] io . Closer ) error {
errs := NewMulti ( )
for _ , c := range cs {
errs . Add ( c . Close ( ) )
2019-03-19 09:31:57 -04:00
}
2020-10-28 11:24:58 -04:00
return errs . Err ( )
2019-03-19 09:31:57 -04:00
}