OLD | NEW |
1 // Copyright 2017 The LUCI Authors. All rights reserved. | 1 // Copyright 2017 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 // Package validation provides a helper for performing config validations. | 5 // Package validation provides a helper for performing config validations. |
6 package validation | 6 package validation |
7 | 7 |
8 import ( | 8 import ( |
9 "fmt" | 9 "fmt" |
10 "strings" | 10 "strings" |
(...skipping 26 matching lines...) Expand all Loading... |
37 // now). Each file may have some internal nested structure. The logical path | 37 // now). Each file may have some internal nested structure. The logical path |
38 // inside this structure is captured through Enter and Exit calls. | 38 // inside this structure is captured through Enter and Exit calls. |
39 type Context struct { | 39 type Context struct { |
40 Logger logging.Logger // logs errors as they appear | 40 Logger logging.Logger // logs errors as they appear |
41 | 41 |
42 errors errors.MultiError // all accumulated errors | 42 errors errors.MultiError // all accumulated errors |
43 file string // the currently validated file | 43 file string // the currently validated file |
44 element []string // logical path of a sub-element we validate,
see Enter | 44 element []string // logical path of a sub-element we validate,
see Enter |
45 } | 45 } |
46 | 46 |
| 47 type fileTagType struct{ Key errors.TagKey } |
| 48 |
| 49 func (f fileTagType) With(name string) errors.TagValue { return errors.MkTagValu
e(f.Key, name) } |
| 50 func (f fileTagType) In(err error) (v string, ok bool) { |
| 51 d, ok := errors.TagValueIn(f.Key, err) |
| 52 if ok { |
| 53 v = d.(string) |
| 54 } |
| 55 return |
| 56 } |
| 57 |
| 58 type elementTagType struct{ Key errors.TagKey } |
| 59 |
| 60 func (e elementTagType) With(elements []string) errors.TagValue { |
| 61 return errors.MkTagValue(e.Key, append([]string(nil), elements...)) |
| 62 } |
| 63 func (e elementTagType) In(err error) (v []string, ok bool) { |
| 64 d, ok := errors.TagValueIn(e.Key, err) |
| 65 if ok { |
| 66 v = d.([]string) |
| 67 } |
| 68 return |
| 69 } |
| 70 |
| 71 var fileTag = fileTagType{errors.NewTagKey("holds the file name for tests")} |
| 72 var elementTag = elementTagType{errors.NewTagKey("holds the elements for tests")
} |
| 73 |
47 // Error records a validation error. | 74 // Error records a validation error. |
48 func (v *Context) Error(msg string, args ...interface{}) { | 75 func (v *Context) Error(msg string, args ...interface{}) { |
49 ctx := "" | 76 ctx := "" |
50 if v.file != "" { | 77 if v.file != "" { |
51 ctx = fmt.Sprintf("in %q", v.file) | 78 ctx = fmt.Sprintf("in %q", v.file) |
52 } else { | 79 } else { |
53 ctx = "in <unspecified file>" | 80 ctx = "in <unspecified file>" |
54 } | 81 } |
55 if len(v.element) != 0 { | 82 if len(v.element) != 0 { |
56 ctx += " (" + strings.Join(v.element, " / ") + ")" | 83 ctx += " (" + strings.Join(v.element, " / ") + ")" |
57 } | 84 } |
58 | 85 |
59 // Prepending ctx to msg before passing it to fmt is not entirely correc
t, | 86 // Prepending ctx to msg before passing it to fmt is not entirely correc
t, |
60 // since ctx may have format specifiers (like %s), that will be misunder
stood. | 87 // since ctx may have format specifiers (like %s), that will be misunder
stood. |
61 // So we put ctx in the argument list. | 88 // So we put ctx in the argument list. |
62 msg = "%s: " + msg | 89 msg = "%s: " + msg |
63 args = append([]interface{}{ctx}, args...) | 90 args = append([]interface{}{ctx}, args...) |
64 if v.Logger != nil { | 91 if v.Logger != nil { |
65 v.Logger.Errorf(msg, args...) | 92 v.Logger.Errorf(msg, args...) |
66 } | 93 } |
67 | 94 |
68 // Make the file and the logical path also usable through error inspecti
on. | 95 // Make the file and the logical path also usable through error inspecti
on. |
69 err := errors.Reason(fmt.Sprintf(msg, args...)). | 96 err := errors.Reason(fmt.Sprintf(msg, args...)). |
70 » » D("file", v.file). | 97 » » Tag(fileTag.With(v.file), elementTag.With(v.element)).Err() |
71 » » D("element", append([]string{}, v.element...)).Err() | |
72 v.errors = append(v.errors, err) | 98 v.errors = append(v.errors, err) |
73 } | 99 } |
74 | 100 |
75 // SetFile records that what follows is errors for this particular file. | 101 // SetFile records that what follows is errors for this particular file. |
76 // | 102 // |
77 // Changing the file resets the current element (see Enter/Exit). | 103 // Changing the file resets the current element (see Enter/Exit). |
78 func (v *Context) SetFile(path string) { | 104 func (v *Context) SetFile(path string) { |
79 if v.file != path { | 105 if v.file != path { |
80 v.file = path | 106 v.file = path |
81 v.element = nil | 107 v.element = nil |
(...skipping 25 matching lines...) Expand all Loading... |
107 // | 133 // |
108 // Returns nil otherwise. | 134 // Returns nil otherwise. |
109 func (v *Context) Finalize() error { | 135 func (v *Context) Finalize() error { |
110 if len(v.errors) == 0 { | 136 if len(v.errors) == 0 { |
111 return nil | 137 return nil |
112 } | 138 } |
113 return &Error{ | 139 return &Error{ |
114 Errors: append(errors.MultiError{}, v.errors...), | 140 Errors: append(errors.MultiError{}, v.errors...), |
115 } | 141 } |
116 } | 142 } |
OLD | NEW |