blob: 4fddf39d8d3e5eaee08b20732d8df8dda3e76111 [file] [log] [blame]
Joe Tsai9834a7d2018-08-01 13:16:49 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package errors implements functions to manipulate errors.
6package errors
7
8import (
9 "fmt"
10 "sort"
11 "strings"
12)
13
14// TODO: This package currently only provides functionality for constructing
15// non-fatal errors. However, it does not currently provide functionality
16// to test for a specific kind of non-fatal error, which is necessary
17// for the end user.
18//
19// When that functionality is added, we need to think carefully about whether
20// a user only cares that some kind of non-fatal error was present or whether
21// all of the errors are of the same kind of non-fatal error.
22
23// NonFatalErrors is a list of non-fatal errors where each error
24// must either be a RequiredNotSet error or InvalidUTF8 error.
25// The list must not be empty.
26type NonFatalErrors []error
27
28func (es NonFatalErrors) Error() string {
29 ms := map[string]struct{}{}
30 for _, e := range es {
31 ms[e.Error()] = struct{}{}
32 }
33 var ss []string
34 for s := range ms {
35 ss = append(ss, s)
36 }
37 sort.Strings(ss)
38 return "proto: " + strings.Join(ss, "; ")
39}
40
41// NonFatal contains non-fatal errors, which are errors that permit execution
42// to continue, but should return with a non-nil error. As such, NonFatal is
43// a data structure useful for swallowing non-fatal errors, but being able to
44// reproduce them at the end of the function.
45// An error is non-fatal if it is collection of non-fatal errors, or is
46// an individual error where IsRequiredNotSet or IsInvalidUTF8 reports true.
47//
48// Typical usage pattern:
49// var nerr errors.NonFatal
50// ...
51// if err := MyFunction(); !nerr.Merge(err) {
52// return nil, err // immediately return if err is fatal
53// }
54// ...
55// return out, nerr.E
56type NonFatal struct{ E error }
57
58// Merge merges err into nf and reports whether it was successful.
59// Otherwise it returns false for any fatal non-nil errors.
60func (nf *NonFatal) Merge(err error) (ok bool) {
61 if err == nil {
62 return true // not an error
63 }
64 if es, ok := err.(NonFatalErrors); ok {
65 nf.append(es...)
66 return true // merged a list of non-fatal errors
67 }
68 if e, ok := err.(interface{ RequiredNotSet() bool }); ok && e.RequiredNotSet() {
69 nf.append(err)
70 return true // non-fatal RequiredNotSet error
71 }
72 if e, ok := err.(interface{ InvalidUTF8() bool }); ok && e.InvalidUTF8() {
73 nf.append(err)
74 return true // non-fatal InvalidUTF8 error
75 }
76 return false // fatal error
77}
78
79// AppendRequiredNotSet appends a RequiredNotSet error.
80func (nf *NonFatal) AppendRequiredNotSet(field string) {
81 nf.append(requiredNotSetError(field))
82}
83
84// AppendInvalidUTF8 appends an InvalidUTF8 error.
85func (nf *NonFatal) AppendInvalidUTF8(field string) {
86 nf.append(invalidUTF8Error(field))
87}
88
89func (nf *NonFatal) append(errs ...error) {
90 es, _ := nf.E.(NonFatalErrors)
91 es = append(es, errs...)
92 nf.E = es
93}
94
95type requiredNotSetError string
96
97func (e requiredNotSetError) Error() string {
98 if e == "" {
99 return "required field not set"
100 }
101 return string("required field " + e + " not set")
102}
103func (requiredNotSetError) RequiredNotSet() bool { return true }
104
105type invalidUTF8Error string
106
107func (e invalidUTF8Error) Error() string {
108 if e == "" {
109 return "invalid UTF-8 detected"
110 }
111 return string("field " + e + " contains invalid UTF-8")
112}
113func (invalidUTF8Error) InvalidUTF8() bool { return true }
114
115// New formats a string according to the format specifier and arguments and
116// returns an error that has a "proto" prefix.
117func New(f string, x ...interface{}) error {
118 for i := 0; i < len(x); i++ {
119 if e, ok := x[i].(prefixError); ok {
120 x[i] = e.s // avoid "proto: " prefix when chaining
121 }
122 }
123 return &prefixError{s: fmt.Sprintf(f, x...)}
124}
125
126type prefixError struct{ s string }
127
128func (e *prefixError) Error() string { return "proto: " + e.s }