prometheus/tsdb/errors/errors.go
Bartlomiej Plotka b5129572cb Fixed typo spotted by Julien.
Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com>
2020-12-23 23:13:49 +00:00

193 lines
5.6 KiB
Go

// 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"
stderrors "errors"
"fmt"
"io"
)
// NilOrMultiError type allows combining multiple errors into one.
type NilOrMultiError struct {
errs []error
}
// NewMulti returns NilOrMultiError with provided errors added if not nil.
func NewMulti(errs ...error) *NilOrMultiError {
m := &NilOrMultiError{}
m.Add(errs...)
return m
}
// Add adds single or many errors to the error list. Each error is added only if not nil.
// If the error is a multiError type, the errors inside multiError are added to the main NilOrMultiError.
func (e *NilOrMultiError) Add(errs ...error) {
for _, err := range errs {
if err == nil {
continue
}
if merr, ok := err.(multiError); ok {
e.errs = append(e.errs, merr.errs...)
continue
}
e.errs = append(e.errs, err)
}
}
// 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 {
return nil
}
return multiError(*e)
}
// Result is extended error interface that allows to use returned read-only multi error in more advanced ways.
type Result interface {
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
}
// 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.
type multiError struct {
errs []error
}
// Errors returns underlying errors.
func (e multiError) Errors() []error {
return e.errs
}
// Error returns a concatenated string of the contained errors.
func (e multiError) Error() string {
var buf bytes.Buffer
if len(e.errs) > 1 {
fmt.Fprintf(&buf, "%d errors: ", len(e.errs))
}
for i, err := range e.errs {
if i != 0 {
buf.WriteString("; ")
}
buf.WriteString(err.Error())
}
return buf.String()
}
// 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
}
// As casts error to multi error read only interface. It returns multi error and true if error matches multi error as
// defined by As method. If returns false if no multi error can be found.
func AsMulti(err error) (Result, bool) {
m := multiError{}
if !stderrors.As(err, &m) {
return nil, false
}
return m, true
}
// CloseAll closes all given closers while recording error in multiError.
func CloseAll(cs []io.Closer) error {
errs := NewMulti()
for _, c := range cs {
errs.Add(c.Close())
}
return errs.Err()
}