OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package gae | |
6 | |
7 import ( | |
8 "fmt" | |
9 "reflect" | |
10 "sync" | |
11 ) | |
12 | |
13 // MultiError is returned by batch operations when there are errors with | |
14 // particular elements. Errors will be in a one-to-one correspondence with | |
15 // the input elements; successful elements will have a nil entry. | |
16 type MultiError []error | |
17 | |
18 func (m MultiError) Error() string { | |
19 s, n := "", 0 | |
20 for _, e := range m { | |
21 if e != nil { | |
22 if n == 0 { | |
23 s = e.Error() | |
24 } | |
25 n++ | |
26 } | |
27 } | |
28 switch n { | |
29 case 0: | |
30 return "(0 errors)" | |
31 case 1: | |
32 return s | |
33 case 2: | |
34 return s + " (and 1 other error)" | |
35 } | |
36 return fmt.Sprintf("%s (and %d other errors)", s, n-1) | |
37 } | |
38 | |
39 // SingleError provides a simple way to uwrap a MultiError if you know that it | |
40 // could only ever contain one element. | |
41 // | |
42 // If err is a MultiError, return its first element. Otherwise, return err. | |
43 func SingleError(err error) error { | |
44 if me, ok := err.(MultiError); ok { | |
45 if len(me) == 0 { | |
46 return nil | |
47 } | |
48 return me[0] | |
49 } | |
50 return err | |
51 } | |
52 | |
53 var ( | |
54 multiErrorType = reflect.TypeOf(MultiError(nil)) | |
55 ) | |
56 | |
57 // FixError will convert a backend-specific non-plain error type to the | |
58 // corresponding gae wrapper type. This is intended to be used solely by | |
59 // implementations (not user code). A correct implementation of the gae wrapper | |
60 // should never return an SDK-specific error type if an alternate type appears | |
61 // in this file. | |
62 func FixError(err error) error { | |
63 if err != nil { | |
64 // we know that err already conforms to the error interface (or
the caller's | |
65 // method wouldn't compile), so check to see if the error's unde
rlying type | |
66 // looks like one of the special error types we implement. | |
67 v := reflect.ValueOf(err) | |
68 if v.Type().ConvertibleTo(multiErrorType) { | |
69 err = v.Convert(multiErrorType).Interface().(error) | |
70 } | |
71 } | |
72 return err | |
73 } | |
74 | |
75 // LazyMultiError is a lazily-constructed MultiError. You specify the target | |
76 // MultiError size up front (as Size), and then you call Assign for each error | |
77 // encountered, and it's potential index. The MultiError will only be allocated | |
78 // if one of the Assign'd errors is non-nil. Similarly, Get will retrieve either | |
79 // the allocated MultiError, or nil if no error was encountered. | |
80 type LazyMultiError struct { | |
81 sync.Mutex | |
82 | |
83 Size int | |
84 me MultiError | |
85 } | |
86 | |
87 // Assign semantically assigns the error to the given index in the MultiError. | |
88 // If the error is nil, no action is taken. Otherwise the MultiError is | |
89 // allocated to its full size (if not already), and the error assigned into it. | |
90 func (e *LazyMultiError) Assign(i int, err error) { | |
91 if err == nil { | |
92 return | |
93 } | |
94 e.Lock() | |
95 defer e.Unlock() | |
96 if e.me == nil { | |
97 e.me = make(MultiError, e.Size) | |
98 } | |
99 e.me[i] = err | |
100 } | |
101 | |
102 // Get returns the MultiError, or nil, if no non-nil error was Assign'd. | |
103 func (e *LazyMultiError) Get() error { | |
104 e.Lock() | |
105 defer e.Unlock() | |
106 if e.me == nil { | |
107 return nil | |
108 } | |
109 return e.me | |
110 } | |
OLD | NEW |