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