-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
137 lines (116 loc) · 3.38 KB
/
Copy patherrors.go
File metadata and controls
137 lines (116 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package confkit
import "strings"
// ErrorKind classifies why a field failed, so callers can react programmatically
// instead of matching on message text.
type ErrorKind string
// The kinds of failure a FieldError can report.
const (
// ErrorKindParse means the raw value could not be converted to the field type.
ErrorKindParse ErrorKind = "parse"
// ErrorKindValidation means the value parsed but broke a `validate` rule.
ErrorKindValidation ErrorKind = "validation"
// ErrorKindIO means a Source failed to retrieve the value at all.
ErrorKindIO ErrorKind = "io"
)
// FieldError describes one problem with one field.
//
// Value holds the offending input, already replaced with a redaction marker when
// the field is tagged `secret:"true"`; Secret records that the field was secret.
// Err, when set, is the underlying cause and makes errors.Is work through the
// enclosing ErrorReport.
type FieldError struct {
Path string
Source string
Kind ErrorKind
Rule string
Message string
Value string
Secret bool
Err error // underlying error, enables errors.Is checks on ErrorReport
}
// ErrorReport collects every field problem found during a single load, so one
// error can report all of them instead of only the first.
//
// It implements error, and Unwrap() []error lets errors.Is and errors.As reach the
// individual causes.
type ErrorReport struct {
Errors []FieldError
}
func (er *ErrorReport) Error() string {
return er.Format()
}
func (er *ErrorReport) Unwrap() []error {
errs := make([]error, len(er.Errors))
for i, fe := range er.Errors {
errs[i] = &singleFieldError{fe}
}
return errs
}
type singleFieldError struct {
FieldError
}
func (e *singleFieldError) Error() string {
return e.Message
}
func (e *singleFieldError) Unwrap() error {
return e.Err
}
// Format renders the report as a multi-line, human-readable message listing each
// field, its source, the rule that failed and the offending value. Values of
// secret fields are shown redacted.
func (er *ErrorReport) Format() string {
if len(er.Errors) == 0 {
return "no errors"
}
var sb strings.Builder
sb.WriteString("Invalid configuration:\n")
for _, err := range er.Errors {
sb.WriteString("\n ")
sb.WriteString(err.Path)
sb.WriteString("\n")
if err.Source != "" {
sb.WriteString(" source: ")
sb.WriteString(err.Source)
sb.WriteString("\n")
}
sb.WriteString(" error: ")
sb.WriteString(err.Message)
sb.WriteString("\n")
if err.Value != "" {
sb.WriteString(" got: ")
if err.Secret {
sb.WriteString("***REDACTED***")
} else {
sb.WriteString(err.Value)
}
sb.WriteString("\n")
}
}
return sb.String()
}
// Explain formats err for display. An *ErrorReport is rendered by Format; any
// other error is returned as its own message. A nil error yields "".
func Explain(err error) string {
if err == nil {
return ""
}
if report, ok := err.(*ErrorReport); ok {
return report.Format()
}
return err.Error()
}
// AddError appends fe to the report.
func (er *ErrorReport) AddError(fe FieldError) {
er.Errors = append(er.Errors, fe)
}
// IsEmpty reports whether the report holds no errors.
func (er *ErrorReport) IsEmpty() bool {
return len(er.Errors) == 0
}
// FirstError returns a pointer to the first error, or nil if the report is empty.
func (er *ErrorReport) FirstError() *FieldError {
if len(er.Errors) == 0 {
return nil
}
return &er.Errors[0]
}