Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Unified Diff: common/errors/multierror.go

Issue 1249933002: Move multierror logic into luci-go. (Closed) Base URL: https://github.com/luci/luci-go@add_mathrand
Patch Set: rename Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « common/errors/markederror_test.go ('k') | common/errors/multierror_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
}
« no previous file with comments | « common/errors/markederror_test.go ('k') | common/errors/multierror_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698