Index: common/errors/multierror.go |
diff --git a/common/errors/multierror.go b/common/errors/multierror.go |
index 30473250dc7ca7bda89d529523eb4472a6c86a6f..afa31333f508543125f3e12dee64e6016dbfda39 100644 |
--- a/common/errors/multierror.go |
+++ b/common/errors/multierror.go |
@@ -6,12 +6,53 @@ package errors |
import ( |
"fmt" |
+ "reflect" |
+ "sync" |
+) |
+ |
+var ( |
+ multiErrorType = reflect.TypeOf(MultiError(nil)) |
) |
// MultiError is a simple `error` implementation which represents multiple |
// `error` objects in one. |
type MultiError []error |
+func (m MultiError) Error() string { |
+ s, n := "", 0 |
+ for _, e := range m { |
+ if e != nil { |
+ if n == 0 { |
+ s = e.Error() |
+ } |
+ n++ |
+ } |
+ } |
+ switch n { |
+ case 0: |
+ return "(0 errors)" |
+ case 1: |
+ return s |
+ case 2: |
+ return s + " (and 1 other error)" |
+ } |
+ return fmt.Sprintf("%s (and %d other errors)", s, n-1) |
+} |
+ |
+// SingleError provides a simple way to uwrap a MultiError if you know that it |
+// could only ever contain one element. |
+// |
+// If err is a MultiError, return its first element. Otherwise, return err. |
+func SingleError(err error) error { |
+ if me, ok := err.(MultiError); ok { |
+ if len(me) == 0 { |
+ return nil |
+ } |
+ return me[0] |
+ } |
+ return err |
+} |
+ |
// MultiErrorFromErrors takes an error-channel, blocks on it, and returns |
// a MultiError for any errors pushed to it over the channel, or nil if |
// all the errors were nil. |
@@ -32,6 +73,54 @@ func MultiErrorFromErrors(ch <-chan error) error { |
return ret |
} |
-func (m MultiError) Error() string { |
- return fmt.Sprintf("%+q", []error(m)) |
+// LazyMultiError is a lazily-constructed MultiError. You specify the target |
+// MultiError size up front (as Size), and then you call Assign for each error |
+// encountered, and it's potential index. The MultiError will only be allocated |
+// if one of the Assign'd errors is non-nil. Similarly, Get will retrieve either |
+// the allocated MultiError, or nil if no error was encountered. |
+type LazyMultiError struct { |
+ sync.Mutex |
+ |
+ Size int |
M-A Ruel
2015/08/10 19:00:21
I don't like that it is exported. I'd prefer to ha
|
+ me MultiError |
+} |
+ |
+// Assign semantically assigns the error to the given index in the MultiError. |
+// If the error is nil, no action is taken. Otherwise the MultiError is |
+// allocated to its full size (if not already), and the error assigned into it. |
+func (e *LazyMultiError) Assign(i int, err error) { |
M-A Ruel
2015/08/10 19:00:21
Why not Add(err error) instead?
|
+ if err == nil { |
+ return |
+ } |
+ e.Lock() |
+ defer e.Unlock() |
+ if e.me == nil { |
+ e.me = make(MultiError, e.Size) |
+ } |
+ e.me[i] = err |
M-A Ruel
2015/08/10 19:00:21
That may crash quite unexpectedly at runtime, espe
|
+} |
+ |
+// Get returns the MultiError, or nil, if no non-nil error was Assign'd. |
+func (e *LazyMultiError) Get() error { |
+ e.Lock() |
+ defer e.Unlock() |
+ if e.me == nil { |
+ return nil |
+ } |
+ return e.me |
+} |
+ |
+// Fix will convert a MultiError compatible type (e.g. []error) to this |
+// version of MultiError. |
+func Fix(err error) error { |
M-A Ruel
2015/08/10 19:00:21
What's wrong with this instead?
if e, ok := err.(
|
+ if err != nil { |
+ // we know that err already conforms to the error interface (or the caller's |
+ // method wouldn't compile), so check to see if the error's underlying type |
+ // looks like one of the special error types we implement. |
+ v := reflect.ValueOf(err) |
+ if v.Type().ConvertibleTo(multiErrorType) { |
+ err = v.Convert(multiErrorType).Interface().(error) |
+ } |
+ } |
+ return err |
} |