mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 02:01:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			137 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) Tailscale Inc & AUTHORS
 | 
						|
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 | 
						|
// Package multierr provides a simple multiple-error type.
 | 
						|
// It was inspired by github.com/go-multierror/multierror.
 | 
						|
package multierr
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"slices"
 | 
						|
	"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 slices.Clone(e.errs)
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
}
 | 
						|
 | 
						|
// 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 {
 | 
						|
	// First count the number of errors to avoid allocating.
 | 
						|
	var n int
 | 
						|
	var errFirst error
 | 
						|
	for _, e := range errs {
 | 
						|
		switch e := e.(type) {
 | 
						|
		case nil:
 | 
						|
			continue
 | 
						|
		case Error:
 | 
						|
			n += len(e.errs)
 | 
						|
			if errFirst == nil && len(e.errs) > 0 {
 | 
						|
				errFirst = e.errs[0]
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			n++
 | 
						|
			if errFirst == nil {
 | 
						|
				errFirst = e
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if n <= 1 {
 | 
						|
		return errFirst // nil if n == 0
 | 
						|
	}
 | 
						|
 | 
						|
	// More than one error, allocate slice and construct the multi-error.
 | 
						|
	dst := make([]error, 0, n)
 | 
						|
	for _, e := range errs {
 | 
						|
		switch e := e.(type) {
 | 
						|
		case nil:
 | 
						|
			continue
 | 
						|
		case Error:
 | 
						|
			dst = append(dst, e.errs...)
 | 
						|
		default:
 | 
						|
			dst = append(dst, e)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	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
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 |