|
|
@@ -0,0 +1,128 @@
|
|
|
+// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
+// Use of this source code is governed by a BSD-style
|
|
|
+// license that can be found in the LICENSE file.
|
|
|
+
|
|
|
+// Package errors implements functions to manipulate errors.
|
|
|
+package errors
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "sort"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+// TODO: This package currently only provides functionality for constructing
|
|
|
+// non-fatal errors. However, it does not currently provide functionality
|
|
|
+// to test for a specific kind of non-fatal error, which is necessary
|
|
|
+// for the end user.
|
|
|
+//
|
|
|
+// When that functionality is added, we need to think carefully about whether
|
|
|
+// a user only cares that some kind of non-fatal error was present or whether
|
|
|
+// all of the errors are of the same kind of non-fatal error.
|
|
|
+
|
|
|
+// NonFatalErrors is a list of non-fatal errors where each error
|
|
|
+// must either be a RequiredNotSet error or InvalidUTF8 error.
|
|
|
+// The list must not be empty.
|
|
|
+type NonFatalErrors []error
|
|
|
+
|
|
|
+func (es NonFatalErrors) Error() string {
|
|
|
+ ms := map[string]struct{}{}
|
|
|
+ for _, e := range es {
|
|
|
+ ms[e.Error()] = struct{}{}
|
|
|
+ }
|
|
|
+ var ss []string
|
|
|
+ for s := range ms {
|
|
|
+ ss = append(ss, s)
|
|
|
+ }
|
|
|
+ sort.Strings(ss)
|
|
|
+ return "proto: " + strings.Join(ss, "; ")
|
|
|
+}
|
|
|
+
|
|
|
+// NonFatal contains non-fatal errors, which are errors that permit execution
|
|
|
+// to continue, but should return with a non-nil error. As such, NonFatal is
|
|
|
+// a data structure useful for swallowing non-fatal errors, but being able to
|
|
|
+// reproduce them at the end of the function.
|
|
|
+// An error is non-fatal if it is collection of non-fatal errors, or is
|
|
|
+// an individual error where IsRequiredNotSet or IsInvalidUTF8 reports true.
|
|
|
+//
|
|
|
+// Typical usage pattern:
|
|
|
+// var nerr errors.NonFatal
|
|
|
+// ...
|
|
|
+// if err := MyFunction(); !nerr.Merge(err) {
|
|
|
+// return nil, err // immediately return if err is fatal
|
|
|
+// }
|
|
|
+// ...
|
|
|
+// return out, nerr.E
|
|
|
+type NonFatal struct{ E error }
|
|
|
+
|
|
|
+// Merge merges err into nf and reports whether it was successful.
|
|
|
+// Otherwise it returns false for any fatal non-nil errors.
|
|
|
+func (nf *NonFatal) Merge(err error) (ok bool) {
|
|
|
+ if err == nil {
|
|
|
+ return true // not an error
|
|
|
+ }
|
|
|
+ if es, ok := err.(NonFatalErrors); ok {
|
|
|
+ nf.append(es...)
|
|
|
+ return true // merged a list of non-fatal errors
|
|
|
+ }
|
|
|
+ if e, ok := err.(interface{ RequiredNotSet() bool }); ok && e.RequiredNotSet() {
|
|
|
+ nf.append(err)
|
|
|
+ return true // non-fatal RequiredNotSet error
|
|
|
+ }
|
|
|
+ if e, ok := err.(interface{ InvalidUTF8() bool }); ok && e.InvalidUTF8() {
|
|
|
+ nf.append(err)
|
|
|
+ return true // non-fatal InvalidUTF8 error
|
|
|
+ }
|
|
|
+ return false // fatal error
|
|
|
+}
|
|
|
+
|
|
|
+// AppendRequiredNotSet appends a RequiredNotSet error.
|
|
|
+func (nf *NonFatal) AppendRequiredNotSet(field string) {
|
|
|
+ nf.append(requiredNotSetError(field))
|
|
|
+}
|
|
|
+
|
|
|
+// AppendInvalidUTF8 appends an InvalidUTF8 error.
|
|
|
+func (nf *NonFatal) AppendInvalidUTF8(field string) {
|
|
|
+ nf.append(invalidUTF8Error(field))
|
|
|
+}
|
|
|
+
|
|
|
+func (nf *NonFatal) append(errs ...error) {
|
|
|
+ es, _ := nf.E.(NonFatalErrors)
|
|
|
+ es = append(es, errs...)
|
|
|
+ nf.E = es
|
|
|
+}
|
|
|
+
|
|
|
+type requiredNotSetError string
|
|
|
+
|
|
|
+func (e requiredNotSetError) Error() string {
|
|
|
+ if e == "" {
|
|
|
+ return "required field not set"
|
|
|
+ }
|
|
|
+ return string("required field " + e + " not set")
|
|
|
+}
|
|
|
+func (requiredNotSetError) RequiredNotSet() bool { return true }
|
|
|
+
|
|
|
+type invalidUTF8Error string
|
|
|
+
|
|
|
+func (e invalidUTF8Error) Error() string {
|
|
|
+ if e == "" {
|
|
|
+ return "invalid UTF-8 detected"
|
|
|
+ }
|
|
|
+ return string("field " + e + " contains invalid UTF-8")
|
|
|
+}
|
|
|
+func (invalidUTF8Error) InvalidUTF8() bool { return true }
|
|
|
+
|
|
|
+// New formats a string according to the format specifier and arguments and
|
|
|
+// returns an error that has a "proto" prefix.
|
|
|
+func New(f string, x ...interface{}) error {
|
|
|
+ for i := 0; i < len(x); i++ {
|
|
|
+ if e, ok := x[i].(prefixError); ok {
|
|
|
+ x[i] = e.s // avoid "proto: " prefix when chaining
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return &prefixError{s: fmt.Sprintf(f, x...)}
|
|
|
+}
|
|
|
+
|
|
|
+type prefixError struct{ s string }
|
|
|
+
|
|
|
+func (e *prefixError) Error() string { return "proto: " + e.s }
|