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 |
| 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) { |
| 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 |
| 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 { |
| 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 |