...
1 package errcode
2
3 import (
4 "encoding/json"
5 "fmt"
6 "strings"
7 )
8
9
10
11 type ErrorCoder interface {
12 ErrorCode() ErrorCode
13 }
14
15
16
17 type ErrorCode int
18
19 var _ error = ErrorCode(0)
20
21
22 func (ec ErrorCode) ErrorCode() ErrorCode {
23 return ec
24 }
25
26
27 func (ec ErrorCode) Error() string {
28
29 return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1))
30 }
31
32
33 func (ec ErrorCode) Descriptor() ErrorDescriptor {
34 d, ok := errorCodeToDescriptors[ec]
35
36 if !ok {
37 return ErrorCodeUnknown.Descriptor()
38 }
39
40 return d
41 }
42
43
44 func (ec ErrorCode) String() string {
45 return ec.Descriptor().Value
46 }
47
48
49 func (ec ErrorCode) Message() string {
50 return ec.Descriptor().Message
51 }
52
53
54
55 func (ec ErrorCode) MarshalText() (text []byte, err error) {
56 return []byte(ec.String()), nil
57 }
58
59
60 func (ec *ErrorCode) UnmarshalText(text []byte) error {
61 desc, ok := idToDescriptors[string(text)]
62
63 if !ok {
64 desc = ErrorCodeUnknown.Descriptor()
65 }
66
67 *ec = desc.Code
68
69 return nil
70 }
71
72
73
74 func (ec ErrorCode) WithMessage(message string) Error {
75 return Error{
76 Code: ec,
77 Message: message,
78 }
79 }
80
81
82
83 func (ec ErrorCode) WithDetail(detail interface{}) Error {
84 return Error{
85 Code: ec,
86 Message: ec.Message(),
87 }.WithDetail(detail)
88 }
89
90
91 func (ec ErrorCode) WithArgs(args ...interface{}) Error {
92 return Error{
93 Code: ec,
94 Message: ec.Message(),
95 }.WithArgs(args...)
96 }
97
98
99 type Error struct {
100 Code ErrorCode `json:"code"`
101 Message string `json:"message"`
102 Detail interface{} `json:"detail,omitempty"`
103
104
105
106 }
107
108 var _ error = Error{}
109
110
111 func (e Error) ErrorCode() ErrorCode {
112 return e.Code
113 }
114
115
116 func (e Error) Error() string {
117 return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message)
118 }
119
120
121
122 func (e Error) WithDetail(detail interface{}) Error {
123 return Error{
124 Code: e.Code,
125 Message: e.Message,
126 Detail: detail,
127 }
128 }
129
130
131
132 func (e Error) WithArgs(args ...interface{}) Error {
133 return Error{
134 Code: e.Code,
135 Message: fmt.Sprintf(e.Code.Message(), args...),
136 Detail: e.Detail,
137 }
138 }
139
140
141 type ErrorDescriptor struct {
142
143 Code ErrorCode
144
145
146
147
148 Value string
149
150
151
152 Message string
153
154
155
156 Description string
157
158
159
160 HTTPStatusCode int
161 }
162
163
164
165 func ParseErrorCode(value string) ErrorCode {
166 ed, ok := idToDescriptors[value]
167 if ok {
168 return ed.Code
169 }
170
171 return ErrorCodeUnknown
172 }
173
174
175
176 type Errors []error
177
178 var _ error = Errors{}
179
180 func (errs Errors) Error() string {
181 switch len(errs) {
182 case 0:
183 return "<nil>"
184 case 1:
185 return errs[0].Error()
186 default:
187 msg := "errors:\n"
188 for _, err := range errs {
189 msg += err.Error() + "\n"
190 }
191 return msg
192 }
193 }
194
195
196 func (errs Errors) Len() int {
197 return len(errs)
198 }
199
200
201
202 func (errs Errors) MarshalJSON() ([]byte, error) {
203 var tmpErrs struct {
204 Errors []Error `json:"errors,omitempty"`
205 }
206
207 for _, daErr := range errs {
208 var err Error
209
210 switch daErr := daErr.(type) {
211 case ErrorCode:
212 err = daErr.WithDetail(nil)
213 case Error:
214 err = daErr
215 default:
216 err = ErrorCodeUnknown.WithDetail(daErr)
217
218 }
219
220
221
222 msg := err.Message
223 if msg == "" {
224 msg = err.Code.Message()
225 }
226
227 tmpErrs.Errors = append(tmpErrs.Errors, Error{
228 Code: err.Code,
229 Message: msg,
230 Detail: err.Detail,
231 })
232 }
233
234 return json.Marshal(tmpErrs)
235 }
236
237
238
239 func (errs *Errors) UnmarshalJSON(data []byte) error {
240 var tmpErrs struct {
241 Errors []Error
242 }
243
244 if err := json.Unmarshal(data, &tmpErrs); err != nil {
245 return err
246 }
247
248 var newErrs Errors
249 for _, daErr := range tmpErrs.Errors {
250
251
252 if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) {
253
254 newErrs = append(newErrs, daErr.Code)
255 } else {
256
257 newErrs = append(newErrs, Error{
258 Code: daErr.Code,
259 Message: daErr.Message,
260 Detail: daErr.Detail,
261 })
262 }
263 }
264
265 *errs = newErrs
266 return nil
267 }
268
View as plain text