-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpanic.go
81 lines (68 loc) · 1.53 KB
/
panic.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
package core
import (
"sync/atomic"
)
// Recovered is an error caught from a panic call
type Recovered interface {
Error() string
Recovered() any
}
// AsRecovered receives the value from recover()
// and wraps it as a Recovered error
func AsRecovered(rvr any) Recovered {
if rvr == nil {
// no panic
return nil
}
if p, ok := rvr.(Recovered); ok {
// pass-through
return p
}
// wrap it
return NewPanicError(2, rvr)
}
// Catcher is a runner that catches panics
type Catcher struct {
recovered atomic.Value
}
// Do calls a function, returning its organic error,
// or the caught panic
func (p *Catcher) Do(fn func() error) error {
if err := p.Try(fn); err != nil {
// natural death
return err
}
if err := p.Recovered(); err != nil {
// recovered panic
return err
}
// all good
return nil
}
// Try calls a function, returning its organic error,
// or storing the recovered error for later consumption
func (p *Catcher) Try(fn func() error) error {
if fn != nil {
defer func() {
if err := AsRecovered(recover()); err != nil {
p.recovered.CompareAndSwap(nil, err)
}
}()
return fn()
}
return nil
}
// Recovered returns the error corresponding to a
// panic when the Catcher was running a function
func (p *Catcher) Recovered() Recovered {
if err, ok := p.recovered.Load().(Recovered); ok {
return err
}
return nil
}
// Catch uses a [Catcher] to safely call a function and
// return the organic error or the [Recovered] [PanicError].
func Catch(fn func() error) error {
var p Catcher
return p.Do(fn)
}