-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexception.go
More file actions
156 lines (134 loc) · 3.64 KB
/
exception.go
File metadata and controls
156 lines (134 loc) · 3.64 KB
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
148
149
150
151
152
153
154
155
156
package exception
import (
"reflect"
"runtime"
"strconv"
"strings"
"github.com/ftloc/caller"
)
type (
// Tryer is the interface that is exposed that encapsules the packages functionality. You may exchange the implementation easily if you deem it necessarily
Tryer interface {
Catch(interface{}) Tryer
CatchAll(func(interface{})) Tryer
Ignore() Tryer
Finally(func())
Go()
}
tryer struct {
mainfn func()
catches map[reflect.Type]interface{}
catchall func(interface{})
}
)
var (
pkgPath = reflect.TypeOf(tryer{}).PkgPath()
)
// Try creates a Tryer object. The given function will be called, when finally on the Tryer object is called.
func Try(mainfn func()) Tryer {
return &tryer{
mainfn: mainfn,
catches: make(map[reflect.Type]interface{}),
catchall: func(i interface{}) { panic(i) },
}
}
// Catch catches exceptions of the type that the given function takes as a first (and only) argument.
func (t *tryer) Catch(fn interface{}) Tryer {
va := reflect.ValueOf(fn)
if va.Kind() != reflect.Func || va.Type().NumIn() != 1 {
panic("Catch needs a function with exactly one parameter (got: " + va.Type().String() + " with " + strconv.Itoa(va.Type().NumIn()) + " arguments)")
}
typ := va.Type().In(0)
t.catches[typ] = fn
return t
}
// CatchAll catches all exceptions and panics that occur within the tried function, that are not specifically caught.
func (t *tryer) CatchAll(fn func(interface{})) Tryer {
t.catchall = fn
return t
}
// Ignore catches all exceptions and silently ignores them
func (t *tryer) Ignore() Tryer {
t.catchall = func(interface{}) {}
return t
}
// Finally initiates the call to the tried function and is always called after
// the function was executed, no matter if an exception occured or not.
func (t *tryer) Finally(finfn func()) {
defer func() {
defer finfn()
if r := recover(); r != nil {
t.findHandler(r)
}
}()
t.mainfn()
}
// This function implements the search for the correct handler
func (t *tryer) findHandler(r interface{}) {
tyo := reflect.TypeOf(r)
if fn, ok := t.catches[tyo]; ok {
caller.CallWith(fn, r)
return
}
for tt, fn := range t.catches {
if tyo.ConvertibleTo(tt) {
caller.CallWith(fn, r)
return
}
}
t.catchall(r)
}
// Go is a shorthand version of Finally(func(){})
func (t *tryer) Go() {
t.Finally(func() {})
}
// Throw an exception. Any type qualifies as an exception.
func Throw(i interface{}) {
panic(i)
}
// ThrowOnFalse throws an exception if the bool b equals false
func ThrowOnFalse(b bool, i interface{}) {
if !b {
Throw(i)
}
}
// ThrowOnFalseFn throws an exception (produced by f) if the bool b equals false
func ThrowOnFalseFn(b bool, f func() interface{}) {
if !b {
Throw(f())
}
}
// ThrowOnError throws an exception if e is not nil
func ThrowOnError(e error, i interface{}) {
if nil != e {
Throw(i)
}
}
// ThrowOnErrorFn throws an exception (produced by f) if e is not nil
func ThrowOnErrorFn(e error, f func() interface{}) {
if nil != e {
Throw(f())
}
}
// GetThrower scans the callstack for the last panic / exception.Throw* call and returns the filename and line, returns false as first parameter if nothing paniced
func GetThrower() (bool, string, int) {
cs := make([]uintptr, 20)
amount := runtime.Callers(2, cs)
usedThrower := false
var pfield *runtime.Func = nil
for i := 0; i < amount; i++ {
f := runtime.FuncForPC(cs[i])
if f.Name() == "runtime.gopanic" {
pfield = f
} else if pfield != nil && strings.HasPrefix(f.Name(), pkgPath+".") {
usedThrower = true
} else if pfield != nil {
if !usedThrower {
return false, "", 0
}
fi, li := f.FileLine(f.Entry())
return true, fi, li
}
}
return false, "", 0
}