b7253fa4a646bbb4bc2ac9a9e2ca96220040f8ca
[scm/test.git] / errors / types.go
1 package errors
2
3 import (
4         "fmt"
5         "net/url"
6
7         "github.com/pkg/errors"
8 )
9
10 // IsFatalError indicates that the error is fatal and the process should exit
11 // immediately after handling the error.
12 func IsFatalError(err error) bool {
13         if e, ok := err.(interface {
14                 Fatal() bool
15         }); ok {
16                 return e.Fatal()
17         }
18         if parent := parentOf(err); parent != nil {
19                 return IsFatalError(parent)
20         }
21         return false
22 }
23
24 // IsNotImplementedError indicates the client attempted to use a feature the
25 // server has not implemented (e.g. the batch endpoint).
26 func IsNotImplementedError(err error) bool {
27         if e, ok := err.(interface {
28                 NotImplemented() bool
29         }); ok {
30                 return e.NotImplemented()
31         }
32         if parent := parentOf(err); parent != nil {
33                 return IsNotImplementedError(parent)
34         }
35         return false
36 }
37
38 // IsAuthError indicates the client provided a request with invalid or no
39 // authentication credentials when credentials are required (e.g. HTTP 401).
40 func IsAuthError(err error) bool {
41         if e, ok := err.(interface {
42                 AuthError() bool
43         }); ok {
44                 return e.AuthError()
45         }
46         if parent := parentOf(err); parent != nil {
47                 return IsAuthError(parent)
48         }
49         return false
50 }
51
52 // IsSmudgeError indicates an error while smudging a files.
53 func IsSmudgeError(err error) bool {
54         if e, ok := err.(interface {
55                 SmudgeError() bool
56         }); ok {
57                 return e.SmudgeError()
58         }
59         if parent := parentOf(err); parent != nil {
60                 return IsSmudgeError(parent)
61         }
62         return false
63 }
64
65 // IsCleanPointerError indicates an error while cleaning a file.
66 func IsCleanPointerError(err error) bool {
67         if e, ok := err.(interface {
68                 CleanPointerError() bool
69         }); ok {
70                 return e.CleanPointerError()
71         }
72         if parent := parentOf(err); parent != nil {
73                 return IsCleanPointerError(parent)
74         }
75         return false
76 }
77
78 // IsNotAPointerError indicates the parsed data is not an LFS pointer.
79 func IsNotAPointerError(err error) bool {
80         if e, ok := err.(interface {
81                 NotAPointerError() bool
82         }); ok {
83                 return e.NotAPointerError()
84         }
85         if parent := parentOf(err); parent != nil {
86                 return IsNotAPointerError(parent)
87         }
88         return false
89 }
90
91 // IsBadPointerKeyError indicates that the parsed data has an invalid key.
92 func IsBadPointerKeyError(err error) bool {
93         if e, ok := err.(interface {
94                 BadPointerKeyError() bool
95         }); ok {
96                 return e.BadPointerKeyError()
97         }
98
99         if parent := parentOf(err); parent != nil {
100                 return IsBadPointerKeyError(parent)
101         }
102         return false
103 }
104
105 // If an error is abad pointer error of any type, returns NotAPointerError
106 func StandardizeBadPointerError(err error) error {
107         if IsBadPointerKeyError(err) {
108                 badErr := err.(badPointerKeyError)
109                 if badErr.Expected == "version" {
110                         return NewNotAPointerError(err)
111                 }
112         }
113         return err
114 }
115
116 // IsDownloadDeclinedError indicates that the smudge operation should not download.
117 // TODO: I don't really like using errors to control that flow, it should be refactored.
118 func IsDownloadDeclinedError(err error) bool {
119         if e, ok := err.(interface {
120                 DownloadDeclinedError() bool
121         }); ok {
122                 return e.DownloadDeclinedError()
123         }
124         if parent := parentOf(err); parent != nil {
125                 return IsDownloadDeclinedError(parent)
126         }
127         return false
128 }
129
130 // IsRetriableError indicates the low level transfer had an error but the
131 // caller may retry the operation.
132 func IsRetriableError(err error) bool {
133         if e, ok := err.(interface {
134                 RetriableError() bool
135         }); ok {
136                 return e.RetriableError()
137         }
138         if cause, ok := Cause(err).(*url.Error); ok {
139                 return cause.Temporary() || cause.Timeout()
140         }
141         if parent := parentOf(err); parent != nil {
142                 return IsRetriableError(parent)
143         }
144         return false
145 }
146
147 type errorWithCause interface {
148         Cause() error
149         StackTrace() errors.StackTrace
150         error
151         fmt.Formatter
152 }
153
154 // wrappedError is the base error wrapper. It provides a Message string, a
155 // stack, and a context map around a regular Go error.
156 type wrappedError struct {
157         errorWithCause
158         context map[string]interface{}
159 }
160
161 // newWrappedError creates a wrappedError.
162 func newWrappedError(err error, message string) *wrappedError {
163         if err == nil {
164                 err = errors.New("Error")
165         }
166
167         var errWithCause errorWithCause
168
169         if len(message) > 0 {
170                 errWithCause = errors.Wrap(err, message).(errorWithCause)
171         } else if ewc, ok := err.(errorWithCause); ok {
172                 errWithCause = ewc
173         } else {
174                 errWithCause = errors.Wrap(err, "LFS").(errorWithCause)
175         }
176
177         return &wrappedError{
178                 context:        make(map[string]interface{}),
179                 errorWithCause: errWithCause,
180         }
181 }
182
183 // Set sets the value for the key in the context.
184 func (e wrappedError) Set(key string, val interface{}) {
185         e.context[key] = val
186 }
187
188 // Get gets the value for a key in the context.
189 func (e wrappedError) Get(key string) interface{} {
190         return e.context[key]
191 }
192
193 // Del removes a key from the context.
194 func (e wrappedError) Del(key string) {
195         delete(e.context, key)
196 }
197
198 // Context returns the underlying context.
199 func (e wrappedError) Context() map[string]interface{} {
200         return e.context
201 }
202
203 // Definitions for IsFatalError()
204
205 type fatalError struct {
206         *wrappedError
207 }
208
209 func (e fatalError) Fatal() bool {
210         return true
211 }
212
213 func NewFatalError(err error) error {
214         return fatalError{newWrappedError(err, "Fatal error")}
215 }
216
217 // Definitions for IsNotImplementedError()
218
219 type notImplementedError struct {
220         *wrappedError
221 }
222
223 func (e notImplementedError) NotImplemented() bool {
224         return true
225 }
226
227 func NewNotImplementedError(err error) error {
228         return notImplementedError{newWrappedError(err, "Not implemented")}
229 }
230
231 // Definitions for IsAuthError()
232
233 type authError struct {
234         *wrappedError
235 }
236
237 func (e authError) AuthError() bool {
238         return true
239 }
240
241 func NewAuthError(err error) error {
242         return authError{newWrappedError(err, "Authentication required")}
243 }
244
245 // Definitions for IsSmudgeError()
246
247 type smudgeError struct {
248         *wrappedError
249 }
250
251 func (e smudgeError) SmudgeError() bool {
252         return true
253 }
254
255 func NewSmudgeError(err error, oid, filename string) error {
256         e := smudgeError{newWrappedError(err, "Smudge error")}
257         SetContext(e, "OID", oid)
258         SetContext(e, "FileName", filename)
259         return e
260 }
261
262 // Definitions for IsCleanPointerError()
263
264 type cleanPointerError struct {
265         *wrappedError
266 }
267
268 func (e cleanPointerError) CleanPointerError() bool {
269         return true
270 }
271
272 func NewCleanPointerError(pointer interface{}, bytes []byte) error {
273         err := New("pointer error")
274         e := cleanPointerError{newWrappedError(err, "clean")}
275         SetContext(e, "pointer", pointer)
276         SetContext(e, "bytes", bytes)
277         return e
278 }
279
280 // Definitions for IsNotAPointerError()
281
282 type notAPointerError struct {
283         *wrappedError
284 }
285
286 func (e notAPointerError) NotAPointerError() bool {
287         return true
288 }
289
290 func NewNotAPointerError(err error) error {
291         return notAPointerError{newWrappedError(err, "Pointer file error")}
292 }
293
294 type badPointerKeyError struct {
295         Expected string
296         Actual   string
297
298         *wrappedError
299 }
300
301 func (e badPointerKeyError) BadPointerKeyError() bool {
302         return true
303 }
304
305 func NewBadPointerKeyError(expected, actual string) error {
306         err := Errorf("Expected key %s, got %s", expected, actual)
307         return badPointerKeyError{expected, actual, newWrappedError(err, "pointer parsing")}
308 }
309
310 // Definitions for IsDownloadDeclinedError()
311
312 type downloadDeclinedError struct {
313         *wrappedError
314 }
315
316 func (e downloadDeclinedError) DownloadDeclinedError() bool {
317         return true
318 }
319
320 func NewDownloadDeclinedError(err error, msg string) error {
321         return downloadDeclinedError{newWrappedError(err, msg)}
322 }
323
324 // Definitions for IsRetriableError()
325
326 type retriableError struct {
327         *wrappedError
328 }
329
330 func (e retriableError) RetriableError() bool {
331         return true
332 }
333
334 func NewRetriableError(err error) error {
335         return retriableError{newWrappedError(err, "")}
336 }
337
338 func parentOf(err error) error {
339         type causer interface {
340                 Cause() error
341         }
342
343         if c, ok := err.(causer); ok {
344                 if innerC, innerOk := c.Cause().(causer); innerOk {
345                         return innerC.Cause()
346                 }
347         }
348
349         return nil
350 }