-
Notifications
You must be signed in to change notification settings - Fork 0
/
errors.go
executable file
·285 lines (218 loc) · 5.73 KB
/
errors.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
package goerrors
import (
"bytes"
"fmt"
"reflect"
"unsafe"
)
var (
// Inheritance cache
errorHierarchies = make(map[string][]string)
)
// IError Interface for extended Go errors
type IError interface {
// This is an error
error
// Get error name
GetName() string
// Get original error
GetSource() error
// Get error message
GetMessage() string
// Get custon data
GetData() interface{}
// Complete try/catch/finally block
Try(try, catch, finally ErrorHandler) error
// Catch error (used as a defered call)
Catch(err *error, catch, finally ErrorHandler)
// Raise error
Raise()
// Test if this error is one of parents of error `err` passed in parameter
IsParentOf(err error) bool
// Get the real reference on this error
getReference() IError
// This method construct the stack trace only in 'Debug' Mode
populateStackTrace(pruneLevels uint)
// Get type of this error
getParents() []string
// Raise error with pruned levels
raise(pruneLevels uint)
}
// ErrorHandler Handler for executing Try, Catch, or Finally block
type ErrorHandler func(err IError) error
// GoError Basic error structure
type GoError struct {
source error // Cause or original error
message string // Error message
trace []string // Stack trace
data interface{} // Custom data
errType reflect.Type // Type of this error
}
// Standard method of `error` interface
func (goErr *GoError) Error() string {
var out bytes.Buffer
err := goErr.getReference()
// Prints error name
_, _ = fmt.Fprintf(&out, "%s: ", err.GetName())
// Get informations
message := err.GetMessage()
source := err.GetSource()
data := err.GetData()
// Prints error informations
if message != "" {
_, _ = fmt.Fprintln(&out, message)
if data != nil {
_, _ = fmt.Fprintln(&out, data)
}
if source != nil {
_, _ = fmt.Fprintln(&out)
_, _ = fmt.Fprintln(&out, "Source:", source)
}
} else {
if source != nil {
_, _ = fmt.Fprintln(&out, source)
}
if data != nil {
_, _ = fmt.Fprintln(&out, data)
}
}
// Prints stack trace only in debug mode
if errDebug {
for _, entry := range goErr.trace {
_, _ = fmt.Fprintln(&out, " ", entry)
}
// Prints a separator if stack trace is not empty
if len(goErr.trace) > 0 {
const sep = "------------------------------------------------------------------------------"
_, _ = fmt.Fprintln(&out, sep)
}
}
// Return content of the buffer resulting from printing theses informations
return out.String()
}
// GetName gets error name
func (goErr *GoError) GetName() string {
return goErr.errType.PkgPath() + "." + goErr.errType.Name()
}
// GetSource gets cause error (parent error)
func (goErr *GoError) GetSource() error {
return goErr.source
}
// GetMessage gets error message
func (goErr *GoError) GetMessage() string {
return goErr.message
}
// GetData gets custon data
func (goErr *GoError) GetData() interface{} {
return goErr.data
}
// Try completes try/catch/finally block
func (goErr *GoError) Try(try, catch, finally ErrorHandler) (err error) {
defer goErr.Catch(&err, catch, finally)
return try(goErr.getReference())
}
// Catch catchs error (used as a defered call)
func (goErr *GoError) Catch(err *error, catch, finally ErrorHandler) {
var resErr error
defer func() {
if finally != nil {
ierr, _ := resErr.(IError)
resErr = finally(ierr)
}
if err != nil {
*err = resErr
}
}()
recovered := recover()
if recovered == nil {
return
}
var ok bool
if resErr, ok = recovered.(error); !ok {
panic(recovered)
}
if this := goErr.getReference(); !this.IsParentOf(resErr) {
panic(recovered)
}
if catch != nil {
resErr = catch(resErr.(IError))
}
}
// Raise for raising error (obviously)
func (goErr *GoError) Raise() {
goErr.raise(1)
}
// IsParentOf tests if this error is one of parents of error `err` passed in parameter
func (goErr *GoError) IsParentOf(err error) bool {
gerr, ok := err.(IError)
if !ok {
return false
}
name := goErr.GetName()
for _, parent := range gerr.getParents() {
if parent == name {
return true
}
}
return false
}
// Init for initializing customized error
func (goErr *GoError) Init(value interface{}, message string, data interface{}, source error, pruneLevels uint) IError {
if goErr.errType == nil {
goErr.setType(value)
goErr.message = message
goErr.data = data
goErr.source = source
goErr.populateStackTrace(pruneLevels + 1)
}
return goErr
}
// Raise the error
func (goErr *GoError) raise(pruneLevels uint) {
res := goErr.getReference()
res.populateStackTrace(pruneLevels + 1)
panic(res)
}
// Define the type of customized error
func (goErr *GoError) setType(value interface{}) {
errType := reflect.ValueOf(value).Type()
if errType.Kind() == reflect.Ptr {
errType = errType.Elem()
}
goErr.errType = errType
}
// Get the real reference on this error
func (goErr *GoError) getReference() IError {
if goErr.errType == nil {
goErr.setType(goErr)
}
ptr := unsafe.Pointer(reflect.ValueOf(goErr).Pointer())
return reflect.NewAt(goErr.errType, ptr).Interface().(IError)
}
// This method construct the stack trace only in 'Debug' Mode
func (goErr *GoError) populateStackTrace(pruneLevels uint) {
// If we aren't in debugging mode,
if !errDebug {
// Do nothing
return
}
goErr.trace = getTrace(pruneLevels + 1)
}
// Get type of this error
func (goErr *GoError) getParents() []string {
name := goErr.GetName()
res, ok := errorHierarchies[name]
if !ok {
res = _getTypeHierarchy(goErr.errType, reflect.TypeOf(goErr).Elem())
errorHierarchies[name] = res
}
return res
}
// GetSource gets the error source from an error, or returns nil if the error passed in argument is not an IError
func GetSource(err error) error {
ierr, ok := err.(IError)
if !ok {
return nil
}
return ierr.GetSource()
}