| 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 |