-
Notifications
You must be signed in to change notification settings - Fork 0
/
unwrap.go
147 lines (137 loc) · 3.17 KB
/
unwrap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package errs
import (
"errors"
"reflect"
)
// Root unwraps err recursively and returns the root error.
func Root(err error) error {
for {
unwrapped := errors.Unwrap(err)
if unwrapped == nil {
return err
}
err = unwrapped
}
}
// Has is a shortcut for errors.As
// when the target error value is not needed.
func Has[T error](err error) bool {
var target T
return errors.As(err, &target)
}
// As returns all errors of type T in the wrapping tree of err.
//
// This function is similar to errors.As
// but traverses the full tree using the interface methods:
//
// Unwrap() error
// Unwrap() []error
func As[T error](err error) []T {
if err == nil {
return nil
}
var errs []T
targetType := reflect.TypeOf((*T)(nil)).Elem()
for {
var target T
if reflect.TypeOf(err).AssignableTo(targetType) {
reflect.ValueOf(&target).Elem().Set(reflect.ValueOf(err))
errs = append(errs, target)
}
if x, ok := err.(interface{ As(any) bool }); ok && x.As(&target) {
errs = append(errs, target)
}
switch x := err.(type) {
case interface{ Unwrap() error }:
err = x.Unwrap()
if err == nil {
return errs
}
case interface{ Unwrap() []error }:
for _, err := range x.Unwrap() {
errs = append(errs, As[T](err)...)
}
return errs
default:
return errs
}
}
}
// // UnwrapAll returns all wrapped errors
// // not including the wrapper errors.
// //
// // It uses the interfaces
// //
// // interface{ Unwrap() error }
// // interface{ Unwrap() []error }
// func UnwrapAll(err error) []error {
// if err == nil {
// return nil
// }
// var errs []error
// for {
// switch x := err.(type) {
// case interface{ Unwrap() error }:
// err = x.Unwrap()
// if err == nil {
// return errs
// }
// errs = append(errs, err)
// case interface{ Unwrap() []error }:
// for _, e := range x.Unwrap() {
// errs = append(errs, UnwrapAll(e)...)
// }
// return errs
// default:
// return append(errs, err)
// }
// }
// }
// UnwrapCallStack unwraps callstack information from err
// and returns the first non callstack wrapper error.
// It does not remove callstack wrapping further down the
// wrapping chain if the top error
// is not wrapped with callstack information.
func UnwrapCallStack(err error) error {
for p, ok := err.(callStackProvider); ok; p, ok = err.(callStackProvider) {
err = p.Unwrap()
}
return err
}
// IsType returns if err or any unwrapped error
// is of the type of the passed ref error.
// It works similar than errors.As but
// without assigning to the ref error
// and without checking for Is or As methods.
func IsType(err, ref error) bool {
if err == ref {
return true
}
if err == nil {
return false
}
t := reflect.TypeOf(ref)
for {
if reflect.TypeOf(err) == t {
return true
}
err = errors.Unwrap(err)
if err == nil {
return false
}
}
}
// Type indicates if err is not nil and it
// or any unwrapped error is of the type T.
// It works similar than errors.As but
// without assigning to the ref error
// and without checking for Is or As methods.
func Type[T error](err error) bool {
for err != nil {
if _, ok := err.(T); ok {
return true
}
err = errors.Unwrap(err)
}
return false
}