summaryrefslogtreecommitdiffstats
path: root/src/errors/wrap.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/errors/wrap.go')
-rw-r--r--src/errors/wrap.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/errors/wrap.go b/src/errors/wrap.go
new file mode 100644
index 0000000..263ae16
--- /dev/null
+++ b/src/errors/wrap.go
@@ -0,0 +1,104 @@
+// Copyright 2018 The Go 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 errors
+
+import (
+ "internal/reflectlite"
+)
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's
+// type contains an Unwrap method returning error.
+// Otherwise, Unwrap returns nil.
+func Unwrap(err error) error {
+ u, ok := err.(interface {
+ Unwrap() error
+ })
+ if !ok {
+ return nil
+ }
+ return u.Unwrap()
+}
+
+// Is reports whether any error in err's chain matches target.
+//
+// The chain consists of err itself followed by the sequence of errors obtained by
+// repeatedly calling Unwrap.
+//
+// An error is considered to match a target if it is equal to that target or if
+// it implements a method Is(error) bool such that Is(target) returns true.
+//
+// An error type might provide an Is method so it can be treated as equivalent
+// to an existing error. For example, if MyError defines
+//
+// func (m MyError) Is(target error) bool { return target == fs.ErrExist }
+//
+// then Is(MyError{}, fs.ErrExist) returns true. See syscall.Errno.Is for
+// an example in the standard library. An Is method should only shallowly
+// compare err and the target and not call Unwrap on either.
+func Is(err, target error) bool {
+ if target == nil {
+ return err == target
+ }
+
+ isComparable := reflectlite.TypeOf(target).Comparable()
+ for {
+ if isComparable && err == target {
+ return true
+ }
+ if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
+ return true
+ }
+ // TODO: consider supporting target.Is(err). This would allow
+ // user-definable predicates, but also may allow for coping with sloppy
+ // APIs, thereby making it easier to get away with them.
+ if err = Unwrap(err); err == nil {
+ return false
+ }
+ }
+}
+
+// As finds the first error in err's chain that matches target, and if one is found, sets
+// target to that error value and returns true. Otherwise, it returns false.
+//
+// The chain consists of err itself followed by the sequence of errors obtained by
+// repeatedly calling Unwrap.
+//
+// An error matches target if the error's concrete value is assignable to the value
+// pointed to by target, or if the error has a method As(interface{}) bool such that
+// As(target) returns true. In the latter case, the As method is responsible for
+// setting target.
+//
+// An error type might provide an As method so it can be treated as if it were a
+// different error type.
+//
+// As panics if target is not a non-nil pointer to either a type that implements
+// error, or to any interface type.
+func As(err error, target any) bool {
+ if target == nil {
+ panic("errors: target cannot be nil")
+ }
+ val := reflectlite.ValueOf(target)
+ typ := val.Type()
+ if typ.Kind() != reflectlite.Ptr || val.IsNil() {
+ panic("errors: target must be a non-nil pointer")
+ }
+ targetType := typ.Elem()
+ if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {
+ panic("errors: *target must be interface or implement error")
+ }
+ for err != nil {
+ if reflectlite.TypeOf(err).AssignableTo(targetType) {
+ val.Elem().Set(reflectlite.ValueOf(err))
+ return true
+ }
+ if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {
+ return true
+ }
+ err = Unwrap(err)
+ }
+ return false
+}
+
+var errorType = reflectlite.TypeOf((*error)(nil)).Elem()