1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package errors
6
7import (
8	"internal/reflectlite"
9)
10
11// Unwrap returns the result of calling the Unwrap method on err, if err's
12// type contains an Unwrap method returning error.
13// Otherwise, Unwrap returns nil.
14//
15// Unwrap only calls a method of the form "Unwrap() error".
16// In particular Unwrap does not unwrap errors returned by [Join].
17func Unwrap(err error) error {
18	u, ok := err.(interface {
19		Unwrap() error
20	})
21	if !ok {
22		return nil
23	}
24	return u.Unwrap()
25}
26
27// Is reports whether any error in err's tree matches target.
28//
29// The tree consists of err itself, followed by the errors obtained by repeatedly
30// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
31// errors, Is examines err followed by a depth-first traversal of its children.
32//
33// An error is considered to match a target if it is equal to that target or if
34// it implements a method Is(error) bool such that Is(target) returns true.
35//
36// An error type might provide an Is method so it can be treated as equivalent
37// to an existing error. For example, if MyError defines
38//
39//	func (m MyError) Is(target error) bool { return target == fs.ErrExist }
40//
41// then Is(MyError{}, fs.ErrExist) returns true. See [syscall.Errno.Is] for
42// an example in the standard library. An Is method should only shallowly
43// compare err and the target and not call [Unwrap] on either.
44func Is(err, target error) bool {
45	if err == nil || target == nil {
46		return err == target
47	}
48
49	isComparable := reflectlite.TypeOf(target).Comparable()
50	return is(err, target, isComparable)
51}
52
53func is(err, target error, targetComparable bool) bool {
54	for {
55		if targetComparable && err == target {
56			return true
57		}
58		if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
59			return true
60		}
61		switch x := err.(type) {
62		case interface{ Unwrap() error }:
63			err = x.Unwrap()
64			if err == nil {
65				return false
66			}
67		case interface{ Unwrap() []error }:
68			for _, err := range x.Unwrap() {
69				if is(err, target, targetComparable) {
70					return true
71				}
72			}
73			return false
74		default:
75			return false
76		}
77	}
78}
79
80// As finds the first error in err's tree that matches target, and if one is found, sets
81// target to that error value and returns true. Otherwise, it returns false.
82//
83// The tree consists of err itself, followed by the errors obtained by repeatedly
84// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
85// errors, As examines err followed by a depth-first traversal of its children.
86//
87// An error matches target if the error's concrete value is assignable to the value
88// pointed to by target, or if the error has a method As(any) bool such that
89// As(target) returns true. In the latter case, the As method is responsible for
90// setting target.
91//
92// An error type might provide an As method so it can be treated as if it were a
93// different error type.
94//
95// As panics if target is not a non-nil pointer to either a type that implements
96// error, or to any interface type.
97func As(err error, target any) bool {
98	if err == nil {
99		return false
100	}
101	if target == nil {
102		panic("errors: target cannot be nil")
103	}
104	val := reflectlite.ValueOf(target)
105	typ := val.Type()
106	if typ.Kind() != reflectlite.Ptr || val.IsNil() {
107		panic("errors: target must be a non-nil pointer")
108	}
109	targetType := typ.Elem()
110	if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {
111		panic("errors: *target must be interface or implement error")
112	}
113	return as(err, target, val, targetType)
114}
115
116func as(err error, target any, targetVal reflectlite.Value, targetType reflectlite.Type) bool {
117	for {
118		if reflectlite.TypeOf(err).AssignableTo(targetType) {
119			targetVal.Elem().Set(reflectlite.ValueOf(err))
120			return true
121		}
122		if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {
123			return true
124		}
125		switch x := err.(type) {
126		case interface{ Unwrap() error }:
127			err = x.Unwrap()
128			if err == nil {
129				return false
130			}
131		case interface{ Unwrap() []error }:
132			for _, err := range x.Unwrap() {
133				if err == nil {
134					continue
135				}
136				if as(err, target, targetVal, targetType) {
137					return true
138				}
139			}
140			return false
141		default:
142			return false
143		}
144	}
145}
146
147var errorType = reflectlite.TypeOf((*error)(nil)).Elem()
148