Imported Upstream version 2.5.1
[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 // IsDownloadDeclinedError indicates that the upload operation failed because of
131 // an HTTP 422 response code.
132 func IsUnprocessableEntityError(err error) bool {
133         if e, ok := err.(interface {
134                 UnprocessableEntityError() bool
135         }); ok {
136                 return e.UnprocessableEntityError()
137         }
138         if parent := parentOf(err); parent != nil {
139                 return IsUnprocessableEntityError(parent)
140         }
141         return false
142 }
143
144 // IsRetriableError indicates the low level transfer had an error but the
145 // caller may retry the operation.
146 func IsRetriableError(err error) bool {
147         if e, ok := err.(interface {
148                 RetriableError() bool
149         }); ok {
150                 return e.RetriableError()
151         }
152         if cause, ok := Cause(err).(*url.Error); ok {
153                 return cause.Temporary() || cause.Timeout()
154         }
155         if parent := parentOf(err); parent != nil {
156                 return IsRetriableError(parent)
157         }
158         return false
159 }
160
161 type errorWithCause interface {
162         Cause() error
163         StackTrace() errors.StackTrace
164         error
165         fmt.Formatter
166 }
167
168 // wrappedError is the base error wrapper. It provides a Message string, a
169 // stack, and a context map around a regular Go error.
170 type wrappedError struct {
171         errorWithCause
172         context map[string]interface{}
173 }
174
175 // newWrappedError creates a wrappedError.
176 func newWrappedError(err error, message string) *wrappedError {
177         if err == nil {
178                 err = errors.New("Error")
179         }
180
181         var errWithCause errorWithCause
182
183         if len(message) > 0 {
184                 errWithCause = errors.Wrap(err, message).(errorWithCause)
185         } else if ewc, ok := err.(errorWithCause); ok {
186                 errWithCause = ewc
187         } else {
188                 errWithCause = errors.Wrap(err, "LFS").(errorWithCause)
189         }
190
191         return &wrappedError{
192                 context:        make(map[string]interface{}),
193                 errorWithCause: errWithCause,
194         }
195 }
196
197 // Set sets the value for the key in the context.
198 func (e wrappedError) Set(key string, val interface{}) {
199         e.context[key] = val
200 }
201
202 // Get gets the value for a key in the context.
203 func (e wrappedError) Get(key string) interface{} {
204         return e.context[key]
205 }
206
207 // Del removes a key from the context.
208 func (e wrappedError) Del(key string) {
209         delete(e.context, key)
210 }
211
212 // Context returns the underlying context.
213 func (e wrappedError) Context() map[string]interface{} {
214         return e.context
215 }
216
217 // Definitions for IsFatalError()
218
219 type fatalError struct {
220         *wrappedError
221 }
222
223 func (e fatalError) Fatal() bool {
224         return true
225 }
226
227 func NewFatalError(err error) error {
228         return fatalError{newWrappedError(err, "Fatal error")}
229 }
230
231 // Definitions for IsNotImplementedError()
232
233 type notImplementedError struct {
234         *wrappedError
235 }
236
237 func (e notImplementedError) NotImplemented() bool {
238         return true
239 }
240
241 func NewNotImplementedError(err error) error {
242         return notImplementedError{newWrappedError(err, "Not implemented")}
243 }
244
245 // Definitions for IsAuthError()
246
247 type authError struct {
248         *wrappedError
249 }
250
251 func (e authError) AuthError() bool {
252         return true
253 }
254
255 func NewAuthError(err error) error {
256         return authError{newWrappedError(err, "Authentication required")}
257 }
258
259 // Definitions for IsSmudgeError()
260
261 type smudgeError struct {
262         *wrappedError
263 }
264
265 func (e smudgeError) SmudgeError() bool {
266         return true
267 }
268
269 func NewSmudgeError(err error, oid, filename string) error {
270         e := smudgeError{newWrappedError(err, "Smudge error")}
271         SetContext(e, "OID", oid)
272         SetContext(e, "FileName", filename)
273         return e
274 }
275
276 // Definitions for IsCleanPointerError()
277
278 type cleanPointerError struct {
279         *wrappedError
280 }
281
282 func (e cleanPointerError) CleanPointerError() bool {
283         return true
284 }
285
286 func NewCleanPointerError(pointer interface{}, bytes []byte) error {
287         err := New("pointer error")
288         e := cleanPointerError{newWrappedError(err, "clean")}
289         SetContext(e, "pointer", pointer)
290         SetContext(e, "bytes", bytes)
291         return e
292 }
293
294 // Definitions for IsNotAPointerError()
295
296 type notAPointerError struct {
297         *wrappedError
298 }
299
300 func (e notAPointerError) NotAPointerError() bool {
301         return true
302 }
303
304 func NewNotAPointerError(err error) error {
305         return notAPointerError{newWrappedError(err, "Pointer file error")}
306 }
307
308 type badPointerKeyError struct {
309         Expected string
310         Actual   string
311
312         *wrappedError
313 }
314
315 func (e badPointerKeyError) BadPointerKeyError() bool {
316         return true
317 }
318
319 func NewBadPointerKeyError(expected, actual string) error {
320         err := Errorf("Expected key %s, got %s", expected, actual)
321         return badPointerKeyError{expected, actual, newWrappedError(err, "pointer parsing")}
322 }
323
324 // Definitions for IsDownloadDeclinedError()
325
326 type downloadDeclinedError struct {
327         *wrappedError
328 }
329
330 func (e downloadDeclinedError) DownloadDeclinedError() bool {
331         return true
332 }
333
334 func NewDownloadDeclinedError(err error, msg string) error {
335         return downloadDeclinedError{newWrappedError(err, msg)}
336 }
337
338 // Definitions for IsUnprocessableEntityError()
339
340 type unprocessableEntityError struct {
341         *wrappedError
342 }
343
344 func (e unprocessableEntityError) UnprocessableEntityError() bool {
345         return true
346 }
347
348 func NewUnprocessableEntityError(err error) error {
349         return unprocessableEntityError{newWrappedError(err, "")}
350 }
351
352 // Definitions for IsRetriableError()
353
354 type retriableError struct {
355         *wrappedError
356 }
357
358 func (e retriableError) RetriableError() bool {
359         return true
360 }
361
362 func NewRetriableError(err error) error {
363         return retriableError{newWrappedError(err, "")}
364 }
365
366 func parentOf(err error) error {
367         type causer interface {
368                 Cause() error
369         }
370
371         if c, ok := err.(causer); ok {
372                 if innerC, innerOk := c.Cause().(causer); innerOk {
373                         return innerC.Cause()
374                 }
375         }
376
377         return nil
378 }