27
28
// RegisterUnsupported records attributes which are not supported by a constraints Value.
28
29
RegisterUnsupported(unsupported []string)
31
// RegisterVocabulary records allowed values for the specified constraint attribute.
32
// allowedValues is expected to be a slice/array but is declared as interface{} so
33
// that vocabs of different types can be passed in.
34
RegisterVocabulary(attributeName string, allowedValues interface{})
30
36
// Validate returns an error if the given constraints are not valid, and also
31
37
// any unsupported attributes.
32
38
Validate(cons Value) ([]string, error)
40
46
func NewValidator() Validator {
42
48
c.conflicts = make(map[string]set.Strings)
49
c.vocab = make(map[string][]interface{})
46
53
type validator struct {
47
54
unsupported set.Strings
48
55
conflicts map[string]set.Strings
56
vocab map[string][]interface{}
51
59
// RegisterConflicts is defined on Validator.
63
71
v.unsupported = set.NewStrings(unsupported...)
74
// RegisterVocabulary is defined on Validator.
75
func (v *validator) RegisterVocabulary(attributeName string, allowedValues interface{}) {
76
k := reflect.TypeOf(allowedValues).Kind()
77
if k != reflect.Slice && k != reflect.Array {
78
panic(fmt.Errorf("invalid vocab: %v of type %T is not a slice", allowedValues, allowedValues))
80
// Convert the vocab to a slice of interface{}
81
var allowedSlice []interface{}
82
val := reflect.ValueOf(allowedValues)
83
for i := 0; i < val.Len(); i++ {
84
allowedSlice = append(allowedSlice, val.Index(i).Interface())
86
v.vocab[attributeName] = allowedSlice
66
89
// checkConflicts returns an error if the constraints Value contains conflicting attributes.
67
90
func (v *validator) checkConflicts(cons Value) error {
68
attrNames := cons.attributesWithValues()
69
attrSet := set.NewStrings(attrNames...)
70
for _, attr := range attrNames {
71
conflicts, ok := v.conflicts[attr]
91
attrValues := cons.attributesWithValues()
92
attrSet := set.NewStrings()
93
for attrTag := range attrValues {
96
for attrTag := range attrValues {
97
conflicts, ok := v.conflicts[attrTag]
75
101
for _, conflict := range conflicts.Values() {
76
102
if attrSet.Contains(conflict) {
77
return fmt.Errorf("ambiguous constraints: %q overlaps with %q", attr, conflict)
103
return fmt.Errorf("ambiguous constraints: %q overlaps with %q", attrTag, conflict)
86
112
return cons.hasAny(v.unsupported.Values()...)
115
// checkValid returns an error if the constraints value contains an attribute value
116
// which is not allowed by the vocab which may have been registered for it.
117
func (v *validator) checkValidValues(cons Value) error {
118
for attrTag, attrValue := range cons.attributesWithValues() {
119
k := reflect.TypeOf(attrValue).Kind()
120
if k == reflect.Slice || k == reflect.Array {
121
// For slices we check that all values are valid.
122
val := reflect.ValueOf(attrValue)
123
for i := 0; i < val.Len(); i++ {
124
if err := v.checkInVocab(attrTag, val.Index(i).Interface()); err != nil {
129
if err := v.checkInVocab(attrTag, attrValue); err != nil {
137
// checkInVocab returns an error if the attribute value is not allowed by the
138
// vocab which may have been registered for it.
139
func (v *validator) checkInVocab(attributeName string, attributeValue interface{}) error {
140
validValues, ok := v.vocab[attributeName]
144
for _, validValue := range validValues {
145
if coerce(validValue) == coerce(attributeValue) {
150
"invalid constraint value: %v=%v\nvalid values are: %v", attributeName, attributeValue, validValues)
153
// coerce returns v in a format that allows constraint values to be easily compared.
154
// Its main purpose is to cast all numeric values to int64 or float64.
155
func coerce(v interface{}) interface{} {
157
switch vv := reflect.TypeOf(v); vv.Kind() {
160
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
161
return int64(reflect.ValueOf(v).Int())
162
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
163
uval := reflect.ValueOf(v).Uint()
164
// Just double check the value is in range.
165
if uval > math.MaxInt64 {
166
panic(fmt.Errorf("constraint value %v is too large", uval))
169
case reflect.Float32, reflect.Float64:
170
return float64(reflect.ValueOf(v).Float())
89
176
// withFallbacks returns a copy of v with nil values taken from vFallback.
90
177
func withFallbacks(v Value, vFallback Value) Value {
91
178
result := vFallback
119
209
return Value{}, err
121
211
// Gather any attributes from consFallback which conflict with those on cons.
122
attrs := cons.attributesWithValues()
212
attrValues := cons.attributesWithValues()
123
213
var fallbackConflicts []string
124
for _, attr := range attrs {
125
fallbackConflicts = append(fallbackConflicts, v.conflicts[attr].Values()...)
214
for attrTag := range attrValues {
215
fallbackConflicts = append(fallbackConflicts, v.conflicts[attrTag].Values()...)
127
217
// Null out the conflicting consFallback attribute values because
128
218
// cons takes priority. We can't error here because we