-
Notifications
You must be signed in to change notification settings - Fork 6
/
errors.go
184 lines (154 loc) · 4.54 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
package page
import (
"bytes"
"context"
"errors"
"fmt"
"runtime"
"time"
)
const MaxStackDepth = 50
// The ErrorPageTemplate type specifies the signature for the error template function. You can replace the built-in error
// function with your own function by setting the config.ErrorPage value. html is the html that was able to be generated before
// the error occurred, which can be helpful in tracking down the source of an error.
type ErrorPageFuncType func(ctx context.Context, html string, err *Error, buf *bytes.Buffer)
var ErrorPageFunc ErrorPageFuncType
// TODO: move to its own package so it is accessible from the entire framework
// The error structure, specifically designed to manage panics during request handling
type Error struct {
// the error string
Err error
// the time the error occurred
Time time.Time
// the copied context when the error occurred
Ctx *Context
// unwound Stack info
Stack []StackFrame
}
// StackFrame holds the file, line and function name in a call chain
type StackFrame struct {
File string
Line int
Func string
}
// DbError represents a database error.
type DbError struct {
Error
// DbStatement is the captured database statement if one caused the error, returned by the db adapter
DbStatement string
}
// NoErr represents no error. A request starts with this.
type NoErr struct {
}
// Known application specific errors
const (
FrameworkErrNone = iota
// FrameworkErrNoTemplate indicates a template does not exist. The control will move on to other ways of rendering.
// No message should be displayed.
FrameworkErrNoTemplate
// FrameworkErrRecordNotFound is a rare situation that might come up as a race condition error between viewing a
// record, and actually editing it. If in the time between clicking on a record to see detail, and viewing the detail,
// the record was deleted by another user, we would return this error.
FrameworkErrRecordNotFound
// A standard situation when someone tries to go to a page they are not authorized to view.
FrameworkErrNotAuthorized
FrameworkErrRedirect
)
// FrameworkError is an expected error that is part of the framework. Usually you would respond to the error
// by displaying a message to the user, but not always.
type FrameworkError struct {
Err int
Location string
}
// NewFrameworkError creates a new FrameworkError
func NewFrameworkError(err int) FrameworkError {
return FrameworkError{Err: err}
}
// Error returns the error string
func (e FrameworkError) Error() string {
switch e.Err {
case FrameworkErrNoTemplate:
return "FormBase or control does not have a template" // just detected, this is not likely to be used
case FrameworkErrRecordNotFound:
return "Record does not exist. Perhaps it has been deleted by someone else?"
case FrameworkErrNotAuthorized:
return "You are not authorized to view this information."
case FrameworkErrRedirect:
return "Redirecting to " + e.Location
}
return ""
}
func RedirectHtml(loc string) string {
return fmt.Sprintf(`<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Refresh" content="0; url=%s" />
</head>
<body>
<p>Redirecting to <a href="%[1]s">%[1]s</a>.</p>
</body>
</html>`, loc)
}
func (e *NoErr) Error() string {
return ""
}
func IsError(e error) bool {
_, ok := e.(*NoErr)
return !ok
}
// Called by the page manager to record a system error
func newRunError(ctx context.Context, msg interface{}) *Error {
e := &Error{}
switch m := msg.(type) {
case string: // we panic'd
e.Err = errors.New(m)
e.fillErr(ctx, 2)
case error: // system generated error
e.Err = m
e.fillErr(ctx, 2)
default:
e.Err = fmt.Errorf("Error of type %T: %v", msg, msg)
e.fillErr(ctx, 1)
}
return e
}
// NewError return a generic message error
func NewError(ctx context.Context, msg string) *Error {
e := &Error{}
e.Err = errors.New(msg)
e.fillErr(ctx, 1)
return e
}
func (e *Error) fillErr(ctx context.Context, skip int) {
e.Time = time.Now()
e.Ctx = GetContext(ctx)
for i := 2 + skip; i < MaxStackDepth; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
name := ""
if f := runtime.FuncForPC(pc); f != nil {
name = f.Name()
}
frame := StackFrame{file, line, name}
e.Stack = append(e.Stack, frame)
}
}
func (e *Error) Error() string {
return e.Err.Error()
}
// NewDbErr returns a new database error
func NewDbErr(ctx context.Context, msg interface{}, dbStatement string) *DbError {
e := &DbError{}
switch m := msg.(type) {
case string:
e.Err = errors.New(m)
case error:
e.Err = m
default:
e.Err = fmt.Errorf("Error of type %T: %v", msg, msg)
}
e.fillErr(ctx, 1)
return e
}