-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexceptions.go
93 lines (87 loc) · 2.34 KB
/
exceptions.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
package exceptions
import "reflect"
// Try executes the given function then invokes all Finally blocks. If the try function panics,
// the panic is recovered and its value is passed to the first Catch block that matches
// the recovered type. If no suitable Catch is found, it panics with the recovered value.
func Try(try func(), thens ...then) {
var (
// remains true if and only if try panics
panicked bool = true
)
defer func() {
defer func() {
for _, then := range thens {
if then.finally != nil {
then.finally()
}
}
}()
if panicked {
cause := recover()
for _, then := range thens {
if then.catch != nil {
if caught := then.catch(cause); caught {
return
}
}
}
panic(cause)
}
}()
try()
panicked = false
}
type then struct {
catch func(any) bool
finally func()
}
// Catch calls the given function with the value recovered from a panic in the Try block.
// Catch blocks are evaluated in order the first one that matches the type of the recovered
// value is called. At most one Catch block will be called for each invocation of Try.
func Catch[C any](catch func(C)) then {
if catch == nil {
catch = func(C) {}
}
return then{
catch: func(cause any) bool {
if c, ok := cause.(C); ok {
catch(c)
return true
}
return false
},
}
}
// CatchNil is a special catch block used for nil panics. CatchNil will catch both typed nils
// (eg: panic((*T)nil) ) and untyped nils (eg: panic(nil) ). In order to detect typed-nils, it
// makes use of the reflec package, So it is not as efficient as Catch. If the cause is an untyped
// nil then `cause == nil` will be true. If the cause is a typed nil then `cause == nil` will be false.
func CatchNil(catch func(any)) then {
return then{
catch: func(cause any) bool {
// Detect untyped-nils
if cause == nil {
catch(cause)
return true
}
// Detect typed-nils
val := reflect.ValueOf(cause)
if val.Kind() == reflect.Ptr && val.IsNil() {
catch(cause)
return true
}
return false
},
}
}
// Finally calls the given function after the Try block completes and before any Catch block
// is run. All Finally blocks are called in the order they are given, whether the Try block
// panics or not.
func Finally(finally func()) then {
if finally == nil {
finally = func() {}
}
return then{
finally: finally,
}
}