2021-11-02 21:30:48 +00:00
|
|
|
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Package multierr provides a simple multiple-error type.
|
|
|
|
// It was inspired by github.com/go-multierror/multierror.
|
|
|
|
package multierr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"strings"
|
2022-12-13 00:48:11 +00:00
|
|
|
|
|
|
|
"golang.org/x/exp/slices"
|
2021-11-02 21:30:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// An Error represents multiple errors.
|
|
|
|
type Error struct {
|
|
|
|
errs []error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error implements the error interface.
|
|
|
|
func (e Error) Error() string {
|
|
|
|
s := new(strings.Builder)
|
|
|
|
s.WriteString("multiple errors:")
|
|
|
|
for _, err := range e.errs {
|
|
|
|
s.WriteString("\n\t")
|
|
|
|
s.WriteString(err.Error())
|
|
|
|
}
|
|
|
|
return s.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Errors returns a slice containing all errors in e.
|
|
|
|
func (e Error) Errors() []error {
|
2022-12-13 00:48:11 +00:00
|
|
|
return slices.Clone(e.errs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(https://go.dev/cl/53435): Implement Unwrap when Go 1.20 is released.
|
|
|
|
/*
|
|
|
|
// Unwrap returns the underlying errors as is.
|
|
|
|
func (e Error) Unwrap() []error {
|
|
|
|
// Do not clone since Unwrap requires callers to not mutate the slice.
|
|
|
|
// See the documentation in the Go "errors" package.
|
|
|
|
return e.errs
|
2021-11-02 21:30:48 +00:00
|
|
|
}
|
2022-12-13 00:48:11 +00:00
|
|
|
*/
|
2021-11-02 21:30:48 +00:00
|
|
|
|
|
|
|
// New returns an error composed from errs.
|
|
|
|
// Some errors in errs get special treatment:
|
2022-08-02 16:33:46 +00:00
|
|
|
// - nil errors are discarded
|
|
|
|
// - errors of type Error are expanded into the top level
|
|
|
|
//
|
2021-11-02 21:30:48 +00:00
|
|
|
// If the resulting slice has length 0, New returns nil.
|
|
|
|
// If the resulting slice has length 1, New returns that error.
|
|
|
|
// If the resulting slice has length > 1, New returns that slice as an Error.
|
|
|
|
func New(errs ...error) error {
|
|
|
|
dst := make([]error, 0, len(errs))
|
|
|
|
for _, e := range errs {
|
|
|
|
switch e := e.(type) {
|
|
|
|
case nil:
|
|
|
|
continue
|
|
|
|
case Error:
|
|
|
|
dst = append(dst, e.errs...)
|
|
|
|
default:
|
|
|
|
dst = append(dst, e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// dst has been filtered and splatted.
|
|
|
|
switch len(dst) {
|
|
|
|
case 0:
|
|
|
|
return nil
|
|
|
|
case 1:
|
|
|
|
return dst[0]
|
|
|
|
}
|
|
|
|
// Zero any remaining elements of dst left over from errs, for GC.
|
|
|
|
tail := dst[len(dst):cap(dst)]
|
|
|
|
for i := range tail {
|
|
|
|
tail[i] = nil
|
|
|
|
}
|
|
|
|
return Error{errs: dst}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is reports whether any error in e matches target.
|
|
|
|
func (e Error) Is(target error) bool {
|
|
|
|
for _, err := range e.errs {
|
|
|
|
if errors.Is(err, target) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// As finds the first error in e that matches target, and if any is found,
|
|
|
|
// sets target to that error value and returns true. Otherwise, it returns false.
|
2022-03-16 23:27:57 +00:00
|
|
|
func (e Error) As(target any) bool {
|
2021-11-02 21:30:48 +00:00
|
|
|
for _, err := range e.errs {
|
|
|
|
if ok := errors.As(err, target); ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2022-12-13 00:48:11 +00:00
|
|
|
|
|
|
|
// Range performs a pre-order, depth-first iteration of the error tree
|
|
|
|
// by successively unwrapping all error values.
|
|
|
|
// For each iteration it calls fn with the current error value and
|
|
|
|
// stops iteration if it ever reports false.
|
|
|
|
func Range(err error, fn func(error) bool) bool {
|
|
|
|
if err == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if !fn(err) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
switch err := err.(type) {
|
|
|
|
case interface{ Unwrap() error }:
|
|
|
|
if err := err.Unwrap(); err != nil {
|
|
|
|
if !Range(err, fn) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case interface{ Unwrap() []error }:
|
|
|
|
for _, err := range err.Unwrap() {
|
|
|
|
if !Range(err, fn) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO(https://go.dev/cl/53435): Delete this when Error implements Unwrap.
|
|
|
|
case Error:
|
|
|
|
for _, err := range err.errs {
|
|
|
|
if !Range(err, fn) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|