mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 00:01:40 +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
 | |
| }
 |