-
Notifications
You must be signed in to change notification settings - Fork 0
/
derp.go
155 lines (122 loc) · 3.73 KB
/
derp.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
package derp
import (
"reflect"
"time"
)
/******************************************
* Constructor Functions
******************************************/
func NewBadRequestError(location string, message string, details ...any) Error {
return New(CodeBadRequestError, location, message, details...)
}
func NewForbiddenError(location string, message string, details ...any) Error {
return New(CodeForbiddenError, location, message, details...)
}
func NewInternalError(location string, message string, details ...any) Error {
return New(CodeInternalError, location, message, details...)
}
func NewNotFoundError(location string, message string, details ...any) Error {
return New(CodeNotFoundError, location, message, details...)
}
func NewUnauthorizedError(location string, message string, details ...any) Error {
return New(CodeUnauthorizedError, location, message, details...)
}
func NewValidationError(message string, details ...any) Error {
return New(CodeValidationError, "", message, details...)
}
// New returns a new Error object
func New(code int, location string, message string, details ...any) Error {
result := Error{
Location: location,
Code: code,
Message: message,
Details: make([]any, 0, len(details)),
TimeStamp: time.Now().Unix(),
}
for _, detail := range details {
if option, ok := detail.(Option); ok {
option(&result)
} else {
result.Details = append(result.Details, detail)
}
}
return result
}
/******************************************
* Data Accessor Functions
******************************************/
// Message retrieves the best-fit error message for any type of error
func Message(err error) string {
if isNil(err) {
return ""
}
if getter, ok := err.(MessageGetter); ok {
return getter.GetMessage()
}
return err.Error()
}
// ErrorCode returns an error code for any error. It tries to read the error code
// from objects matching the ErrorCodeGetter interface. If the provided error does not
// match this interface, then it assigns a generic "Internal Server Error" code 500.
func ErrorCode(err error) int {
if isNil(err) {
return 0
}
if getter, ok := err.(ErrorCodeGetter); ok {
return getter.GetErrorCode()
}
return CodeInternalError
}
/******************************************
* Other Manipulations
******************************************/
// Wrap encapsulates an existing derp.Error
func Wrap(inner error, location string, message string, details ...any) error {
// If the inner error is nil, then the wrapped error is nil, too.
if isNil(inner) {
return nil
}
// If the inner error is not of a known type, then serialize it into the details.
switch inner.(type) {
case Error:
default:
details = append(details, inner.Error())
}
result := Error{
WrappedValue: inner,
Location: location,
Message: message,
Details: make([]any, 0, len(details)),
TimeStamp: time.Now().Unix(),
Code: ErrorCode(inner),
}
for _, detail := range details {
if option, ok := detail.(Option); ok {
option(&result)
} else {
result.Details = append(result.Details, detail)
}
}
return result
}
// ReportAndReturn reports an error to the logger
// and also returns it to the caller.
func ReportAndReturn(err error) error {
Report(err)
return err
}
/******************************************
* Other Helpers
******************************************/
// isNil performs a robust nil check on an error interface
// Shout out to: https://medium.com/@mangatmodi/go-check-nil-interface-the-right-way-d142776edef1
func isNil(i error) bool {
if i == nil {
return true
}
switch reflect.TypeOf(i).Kind() {
case reflect.Ptr, reflect.Array, reflect.Slice, reflect.Chan, reflect.Map:
return reflect.ValueOf(i).IsNil()
}
return false
}