Chromium Code Reviews| 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 |