1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package validate
16
17 import (
18 stderrors "errors"
19 "reflect"
20 "strings"
21
22 "github.com/go-openapi/errors"
23 "github.com/go-openapi/spec"
24 )
25
26 var emptyResult = &Result{MatchCount: 1}
27
28
29
30
31
32
33
34
35
36
37
38
39
40 type Result struct {
41 Errors []error
42 Warnings []error
43 MatchCount int
44
45
46 data interface{}
47
48
49 rootObjectSchemata schemata
50
51 fieldSchemata []fieldSchemata
52
53 itemSchemata []itemSchemata
54
55 cachedFieldSchemata map[FieldKey][]*spec.Schema
56 cachedItemSchemata map[ItemKey][]*spec.Schema
57
58 wantsRedeemOnMerge bool
59 }
60
61
62 type FieldKey struct {
63 object reflect.Value
64 field string
65 }
66
67
68 type ItemKey struct {
69 slice reflect.Value
70 index int
71 }
72
73
74 func NewFieldKey(obj map[string]interface{}, field string) FieldKey {
75 return FieldKey{object: reflect.ValueOf(obj), field: field}
76 }
77
78
79 func (fk *FieldKey) Object() map[string]interface{} {
80 return fk.object.Interface().(map[string]interface{})
81 }
82
83
84 func (fk *FieldKey) Field() string {
85 return fk.field
86 }
87
88
89 func NewItemKey(slice interface{}, i int) ItemKey {
90 return ItemKey{slice: reflect.ValueOf(slice), index: i}
91 }
92
93
94 func (ik *ItemKey) Slice() []interface{} {
95 return ik.slice.Interface().([]interface{})
96 }
97
98
99 func (ik *ItemKey) Index() int {
100 return ik.index
101 }
102
103 type fieldSchemata struct {
104 obj map[string]interface{}
105 field string
106 schemata schemata
107 }
108
109 type itemSchemata struct {
110 slice reflect.Value
111 index int
112 schemata schemata
113 }
114
115
116 func (r *Result) Merge(others ...*Result) *Result {
117 for _, other := range others {
118 if other == nil {
119 continue
120 }
121 r.mergeWithoutRootSchemata(other)
122 r.rootObjectSchemata.Append(other.rootObjectSchemata)
123 if other.wantsRedeemOnMerge {
124 pools.poolOfResults.RedeemResult(other)
125 }
126 }
127 return r
128 }
129
130
131
132 func (r *Result) Data() interface{} {
133 return r.data
134 }
135
136
137 func (r *Result) RootObjectSchemata() []*spec.Schema {
138 return r.rootObjectSchemata.Slice()
139 }
140
141
142 func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
143 if r.cachedFieldSchemata != nil {
144 return r.cachedFieldSchemata
145 }
146
147 ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata))
148 for _, fs := range r.fieldSchemata {
149 key := NewFieldKey(fs.obj, fs.field)
150 if fs.schemata.one != nil {
151 ret[key] = append(ret[key], fs.schemata.one)
152 } else if len(fs.schemata.multiple) > 0 {
153 ret[key] = append(ret[key], fs.schemata.multiple...)
154 }
155 }
156 r.cachedFieldSchemata = ret
157
158 return ret
159 }
160
161
162 func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
163 if r.cachedItemSchemata != nil {
164 return r.cachedItemSchemata
165 }
166
167 ret := make(map[ItemKey][]*spec.Schema, len(r.itemSchemata))
168 for _, ss := range r.itemSchemata {
169 key := NewItemKey(ss.slice, ss.index)
170 if ss.schemata.one != nil {
171 ret[key] = append(ret[key], ss.schemata.one)
172 } else if len(ss.schemata.multiple) > 0 {
173 ret[key] = append(ret[key], ss.schemata.multiple...)
174 }
175 }
176 r.cachedItemSchemata = ret
177 return ret
178 }
179
180 func (r *Result) resetCaches() {
181 r.cachedFieldSchemata = nil
182 r.cachedItemSchemata = nil
183 }
184
185
186
187
188 func (r *Result) mergeForField(obj map[string]interface{}, field string, other *Result) *Result {
189 if other == nil {
190 return r
191 }
192 r.mergeWithoutRootSchemata(other)
193
194 if other.rootObjectSchemata.Len() > 0 {
195 if r.fieldSchemata == nil {
196 r.fieldSchemata = make([]fieldSchemata, len(obj))
197 }
198
199 r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{
200 obj: obj,
201 field: field,
202 schemata: other.rootObjectSchemata.Clone(),
203 })
204 }
205 if other.wantsRedeemOnMerge {
206 pools.poolOfResults.RedeemResult(other)
207 }
208
209 return r
210 }
211
212
213
214
215 func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Result {
216 if other == nil {
217 return r
218 }
219 r.mergeWithoutRootSchemata(other)
220
221 if other.rootObjectSchemata.Len() > 0 {
222 if r.itemSchemata == nil {
223 r.itemSchemata = make([]itemSchemata, slice.Len())
224 }
225
226 r.itemSchemata = append(r.itemSchemata, itemSchemata{
227 slice: slice,
228 index: i,
229 schemata: other.rootObjectSchemata.Clone(),
230 })
231 }
232
233 if other.wantsRedeemOnMerge {
234 pools.poolOfResults.RedeemResult(other)
235 }
236
237 return r
238 }
239
240
241
242
243 func (r *Result) addRootObjectSchemata(s *spec.Schema) {
244 clone := *s
245 r.rootObjectSchemata.Append(schemata{one: &clone})
246 }
247
248
249
250
251 func (r *Result) addPropertySchemata(obj map[string]interface{}, fld string, schema *spec.Schema) {
252 if r.fieldSchemata == nil {
253 r.fieldSchemata = make([]fieldSchemata, 0, len(obj))
254 }
255 clone := *schema
256 r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: &clone}})
257 }
258
259
269
270
271 func (r *Result) mergeWithoutRootSchemata(other *Result) {
272 r.resetCaches()
273 r.AddErrors(other.Errors...)
274 r.AddWarnings(other.Warnings...)
275 r.MatchCount += other.MatchCount
276
277 if other.fieldSchemata != nil {
278 if r.fieldSchemata == nil {
279 r.fieldSchemata = make([]fieldSchemata, 0, len(other.fieldSchemata))
280 }
281 for _, field := range other.fieldSchemata {
282 field.schemata = field.schemata.Clone()
283 r.fieldSchemata = append(r.fieldSchemata, field)
284 }
285 }
286
287 if other.itemSchemata != nil {
288 if r.itemSchemata == nil {
289 r.itemSchemata = make([]itemSchemata, 0, len(other.itemSchemata))
290 }
291 for _, field := range other.itemSchemata {
292 field.schemata = field.schemata.Clone()
293 r.itemSchemata = append(r.itemSchemata, field)
294 }
295 }
296 }
297
298
299
300
301 func (r *Result) MergeAsErrors(others ...*Result) *Result {
302 for _, other := range others {
303 if other != nil {
304 r.resetCaches()
305 r.AddErrors(other.Errors...)
306 r.AddErrors(other.Warnings...)
307 r.MatchCount += other.MatchCount
308 if other.wantsRedeemOnMerge {
309 pools.poolOfResults.RedeemResult(other)
310 }
311 }
312 }
313 return r
314 }
315
316
317
318
319 func (r *Result) MergeAsWarnings(others ...*Result) *Result {
320 for _, other := range others {
321 if other != nil {
322 r.resetCaches()
323 r.AddWarnings(other.Errors...)
324 r.AddWarnings(other.Warnings...)
325 r.MatchCount += other.MatchCount
326 if other.wantsRedeemOnMerge {
327 pools.poolOfResults.RedeemResult(other)
328 }
329 }
330 }
331 return r
332 }
333
334
335
336
337
338
339 func (r *Result) AddErrors(errors ...error) {
340 for _, e := range errors {
341 found := false
342 if e != nil {
343 for _, isReported := range r.Errors {
344 if e.Error() == isReported.Error() {
345 found = true
346 break
347 }
348 }
349 if !found {
350 r.Errors = append(r.Errors, e)
351 }
352 }
353 }
354 }
355
356
357 func (r *Result) AddWarnings(warnings ...error) {
358 for _, e := range warnings {
359 found := false
360 if e != nil {
361 for _, isReported := range r.Warnings {
362 if e.Error() == isReported.Error() {
363 found = true
364 break
365 }
366 }
367 if !found {
368 r.Warnings = append(r.Warnings, e)
369 }
370 }
371 }
372 }
373
374 func (r *Result) keepRelevantErrors() *Result {
375
376
377
378
379
380
381
382
383
384
385
386
387 strippedErrors := []error{}
388 for _, e := range r.Errors {
389 if strings.HasPrefix(e.Error(), "IMPORTANT!") {
390 strippedErrors = append(strippedErrors, stderrors.New(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
391 }
392 }
393 strippedWarnings := []error{}
394 for _, e := range r.Warnings {
395 if strings.HasPrefix(e.Error(), "IMPORTANT!") {
396 strippedWarnings = append(strippedWarnings, stderrors.New(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
397 }
398 }
399 var strippedResult *Result
400 if r.wantsRedeemOnMerge {
401 strippedResult = pools.poolOfResults.BorrowResult()
402 } else {
403 strippedResult = new(Result)
404 }
405 strippedResult.Errors = strippedErrors
406 strippedResult.Warnings = strippedWarnings
407 return strippedResult
408 }
409
410
411
412
413 func (r *Result) IsValid() bool {
414 if r == nil {
415 return true
416 }
417 return len(r.Errors) == 0
418 }
419
420
421
422
423 func (r *Result) HasErrors() bool {
424 if r == nil {
425 return false
426 }
427 return !r.IsValid()
428 }
429
430
431
432
433 func (r *Result) HasWarnings() bool {
434 if r == nil {
435 return false
436 }
437 return len(r.Warnings) > 0
438 }
439
440
441
442
443
444 func (r *Result) HasErrorsOrWarnings() bool {
445 if r == nil {
446 return false
447 }
448 return len(r.Errors) > 0 || len(r.Warnings) > 0
449 }
450
451
452 func (r *Result) Inc() {
453 r.MatchCount++
454 }
455
456
457
458
459 func (r *Result) AsError() error {
460 if r.IsValid() {
461 return nil
462 }
463 return errors.CompositeValidationError(r.Errors...)
464 }
465
466 func (r *Result) cleared() *Result {
467
468 r.Errors = r.Errors[:0]
469 r.Warnings = r.Warnings[:0]
470 r.MatchCount = 0
471 r.data = nil
472 r.rootObjectSchemata.one = nil
473 r.rootObjectSchemata.multiple = r.rootObjectSchemata.multiple[:0]
474 r.fieldSchemata = r.fieldSchemata[:0]
475 r.itemSchemata = r.itemSchemata[:0]
476 for k := range r.cachedFieldSchemata {
477 delete(r.cachedFieldSchemata, k)
478 }
479 for k := range r.cachedItemSchemata {
480 delete(r.cachedItemSchemata, k)
481 }
482 r.wantsRedeemOnMerge = true
483
484 return r
485 }
486
487
488
489 type schemata struct {
490
491 one *spec.Schema
492
493 multiple []*spec.Schema
494 }
495
496 func (s *schemata) Len() int {
497 if s.one != nil {
498 return 1
499 }
500 return len(s.multiple)
501 }
502
503 func (s *schemata) Slice() []*spec.Schema {
504 if s == nil {
505 return nil
506 }
507 if s.one != nil {
508 return []*spec.Schema{s.one}
509 }
510 return s.multiple
511 }
512
513
514 func (s *schemata) Append(other schemata) {
515 if other.one == nil && len(other.multiple) == 0 {
516 return
517 }
518 if s.one == nil && len(s.multiple) == 0 {
519 *s = other
520 return
521 }
522
523 if s.one != nil {
524 if other.one != nil {
525 s.multiple = []*spec.Schema{s.one, other.one}
526 } else {
527 t := make([]*spec.Schema, 0, 1+len(other.multiple))
528 s.multiple = append(append(t, s.one), other.multiple...)
529 }
530 s.one = nil
531 } else {
532 if other.one != nil {
533 s.multiple = append(s.multiple, other.one)
534 } else {
535 if cap(s.multiple) >= len(s.multiple)+len(other.multiple) {
536 s.multiple = append(s.multiple, other.multiple...)
537 } else {
538 t := make([]*spec.Schema, 0, len(s.multiple)+len(other.multiple))
539 s.multiple = append(append(t, s.multiple...), other.multiple...)
540 }
541 }
542 }
543 }
544
545 func (s schemata) Clone() schemata {
546 var clone schemata
547
548 if s.one != nil {
549 clone.one = new(spec.Schema)
550 *clone.one = *s.one
551 }
552
553 if len(s.multiple) > 0 {
554 clone.multiple = make([]*spec.Schema, len(s.multiple))
555 for idx := 0; idx < len(s.multiple); idx++ {
556 sp := new(spec.Schema)
557 *sp = *s.multiple[idx]
558 clone.multiple[idx] = sp
559 }
560 }
561
562 return clone
563 }
564
View as plain text