OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package errors | 5 package errors |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
9 "reflect" | |
10 "sync" | |
11 ) | |
12 | |
13 var ( | |
14 multiErrorType = reflect.TypeOf(MultiError(nil)) | |
9 ) | 15 ) |
10 | 16 |
11 // MultiError is a simple `error` implementation which represents multiple | 17 // MultiError is a simple `error` implementation which represents multiple |
12 // `error` objects in one. | 18 // `error` objects in one. |
13 type MultiError []error | 19 type MultiError []error |
14 | 20 |
21 func (m MultiError) Error() string { | |
22 s, n := "", 0 | |
23 for _, e := range m { | |
24 if e != nil { | |
25 if n == 0 { | |
26 s = e.Error() | |
27 } | |
28 n++ | |
29 } | |
30 } | |
31 switch n { | |
32 case 0: | |
33 return "(0 errors)" | |
34 case 1: | |
35 return s | |
36 case 2: | |
37 return s + " (and 1 other error)" | |
38 } | |
39 return fmt.Sprintf("%s (and %d other errors)", s, n-1) | |
40 } | |
41 | |
42 // SingleError provides a simple way to uwrap a MultiError if you know that it | |
43 // could only ever contain one element. | |
44 // | |
45 // If err is a MultiError, return its first element. Otherwise, return err. | |
46 func SingleError(err error) error { | |
47 if me, ok := err.(MultiError); ok { | |
48 if len(me) == 0 { | |
49 return nil | |
50 } | |
51 return me[0] | |
52 } | |
53 return err | |
54 } | |
55 | |
15 // MultiErrorFromErrors takes an error-channel, blocks on it, and returns | 56 // MultiErrorFromErrors takes an error-channel, blocks on it, and returns |
16 // a MultiError for any errors pushed to it over the channel, or nil if | 57 // a MultiError for any errors pushed to it over the channel, or nil if |
17 // all the errors were nil. | 58 // all the errors were nil. |
18 func MultiErrorFromErrors(ch <-chan error) error { | 59 func MultiErrorFromErrors(ch <-chan error) error { |
19 if ch == nil { | 60 if ch == nil { |
20 return nil | 61 return nil |
21 } | 62 } |
22 ret := MultiError(nil) | 63 ret := MultiError(nil) |
23 for e := range ch { | 64 for e := range ch { |
24 if e == nil { | 65 if e == nil { |
25 continue | 66 continue |
26 } | 67 } |
27 ret = append(ret, e) | 68 ret = append(ret, e) |
28 } | 69 } |
29 if len(ret) == 0 { | 70 if len(ret) == 0 { |
30 return nil | 71 return nil |
31 } | 72 } |
32 return ret | 73 return ret |
33 } | 74 } |
34 | 75 |
35 func (m MultiError) Error() string { | 76 // LazyMultiError is a lazily-constructed MultiError. You specify the target |
36 » return fmt.Sprintf("%+q", []error(m)) | 77 // MultiError size up front (as Size), and then you call Assign for each error |
78 // encountered, and it's potential index. The MultiError will only be allocated | |
79 // if one of the Assign'd errors is non-nil. Similarly, Get will retrieve either | |
80 // the allocated MultiError, or nil if no error was encountered. | |
81 type LazyMultiError struct { | |
82 » sync.Mutex | |
83 | |
84 » Size int | |
M-A Ruel
2015/08/10 19:00:21
I don't like that it is exported. I'd prefer to ha
| |
85 » me MultiError | |
37 } | 86 } |
87 | |
88 // Assign semantically assigns the error to the given index in the MultiError. | |
89 // If the error is nil, no action is taken. Otherwise the MultiError is | |
90 // allocated to its full size (if not already), and the error assigned into it. | |
91 func (e *LazyMultiError) Assign(i int, err error) { | |
M-A Ruel
2015/08/10 19:00:21
Why not Add(err error) instead?
| |
92 if err == nil { | |
93 return | |
94 } | |
95 e.Lock() | |
96 defer e.Unlock() | |
97 if e.me == nil { | |
98 e.me = make(MultiError, e.Size) | |
99 } | |
100 e.me[i] = err | |
M-A Ruel
2015/08/10 19:00:21
That may crash quite unexpectedly at runtime, espe
| |
101 } | |
102 | |
103 // Get returns the MultiError, or nil, if no non-nil error was Assign'd. | |
104 func (e *LazyMultiError) Get() error { | |
105 e.Lock() | |
106 defer e.Unlock() | |
107 if e.me == nil { | |
108 return nil | |
109 } | |
110 return e.me | |
111 } | |
112 | |
113 // Fix will convert a MultiError compatible type (e.g. []error) to this | |
114 // version of MultiError. | |
115 func Fix(err error) error { | |
M-A Ruel
2015/08/10 19:00:21
What's wrong with this instead?
if e, ok := err.(
| |
116 if err != nil { | |
117 // we know that err already conforms to the error interface (or the caller's | |
118 // method wouldn't compile), so check to see if the error's unde rlying type | |
119 // looks like one of the special error types we implement. | |
120 v := reflect.ValueOf(err) | |
121 if v.Type().ConvertibleTo(multiErrorType) { | |
122 err = v.Convert(multiErrorType).Interface().(error) | |
123 } | |
124 } | |
125 return err | |
126 } | |
OLD | NEW |