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 var fileTag = errors.NewTagger("holds the file name for tests") |
| 48 var element0Tag = errors.NewTagger("holds the first element for tests") |
| 49 |
47 // Error records a validation error. | 50 // Error records a validation error. |
48 func (v *Context) Error(msg string, args ...interface{}) { | 51 func (v *Context) Error(msg string, args ...interface{}) { |
49 ctx := "" | 52 ctx := "" |
50 if v.file != "" { | 53 if v.file != "" { |
51 ctx = fmt.Sprintf("in %q", v.file) | 54 ctx = fmt.Sprintf("in %q", v.file) |
52 } else { | 55 } else { |
53 ctx = "in <unspecified file>" | 56 ctx = "in <unspecified file>" |
54 } | 57 } |
| 58 tags := []errors.TagValue{fileTag.With(v.file)} |
55 if len(v.element) != 0 { | 59 if len(v.element) != 0 { |
| 60 tags = append(tags, element0Tag.With(v.element[0])) |
56 ctx += " (" + strings.Join(v.element, " / ") + ")" | 61 ctx += " (" + strings.Join(v.element, " / ") + ")" |
57 } | 62 } |
58 | 63 |
59 // Prepending ctx to msg before passing it to fmt is not entirely correc
t, | 64 // 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. | 65 // since ctx may have format specifiers (like %s), that will be misunder
stood. |
61 // So we put ctx in the argument list. | 66 // So we put ctx in the argument list. |
62 msg = "%s: " + msg | 67 msg = "%s: " + msg |
63 args = append([]interface{}{ctx}, args...) | 68 args = append([]interface{}{ctx}, args...) |
64 if v.Logger != nil { | 69 if v.Logger != nil { |
65 v.Logger.Errorf(msg, args...) | 70 v.Logger.Errorf(msg, args...) |
66 } | 71 } |
67 | 72 |
68 // Make the file and the logical path also usable through error inspecti
on. | 73 // Make the file and the logical path also usable through error inspecti
on. |
69 » err := errors.Reason(fmt.Sprintf(msg, args...)). | 74 » err := errors.Reason(fmt.Sprintf(msg, args...)).Tag(tags...).Err() |
70 » » D("file", v.file). | |
71 » » D("element", append([]string{}, v.element...)).Err() | |
72 v.errors = append(v.errors, err) | 75 v.errors = append(v.errors, err) |
73 } | 76 } |
74 | 77 |
75 // SetFile records that what follows is errors for this particular file. | 78 // SetFile records that what follows is errors for this particular file. |
76 // | 79 // |
77 // Changing the file resets the current element (see Enter/Exit). | 80 // Changing the file resets the current element (see Enter/Exit). |
78 func (v *Context) SetFile(path string) { | 81 func (v *Context) SetFile(path string) { |
79 if v.file != path { | 82 if v.file != path { |
80 v.file = path | 83 v.file = path |
81 v.element = nil | 84 v.element = nil |
(...skipping 25 matching lines...) Expand all Loading... |
107 // | 110 // |
108 // Returns nil otherwise. | 111 // Returns nil otherwise. |
109 func (v *Context) Finalize() error { | 112 func (v *Context) Finalize() error { |
110 if len(v.errors) == 0 { | 113 if len(v.errors) == 0 { |
111 return nil | 114 return nil |
112 } | 115 } |
113 return &Error{ | 116 return &Error{ |
114 Errors: append(errors.MultiError{}, v.errors...), | 117 Errors: append(errors.MultiError{}, v.errors...), |
115 } | 118 } |
116 } | 119 } |
OLD | NEW |