90 lines
2.1 KiB
Go
90 lines
2.1 KiB
Go
// 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"
|
|
)
|
|
|
|
// 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 {
|
|
return append(e.errs[:0:0], e.errs...)
|
|
}
|
|
|
|
// New returns an error composed from errs.
|
|
// Some errors in errs get special treatment:
|
|
// - nil errors are discarded
|
|
// - errors of type Error are expanded into the top level
|
|
//
|
|
// 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.
|
|
func (e Error) As(target any) bool {
|
|
for _, err := range e.errs {
|
|
if ok := errors.As(err, target); ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|