1#![allow(non_snake_case)]
2#![allow(clippy::too_many_arguments)]
3
4use serde_json::{json, Map, Value, Value::Array, Value::Bool, Value::Object};
5
6use crate::config::Config;
7use crate::context::Context;
8use crate::error::{make_error, no_error, ErrorIterator, ValidationError};
9use crate::unique;
10use crate::util;
11
12pub type Validator<'a> = fn(
34 cfg: &'a Config<'a>,
35 instance: &'a Value,
36 schema: &'a Value,
37 parent_schema: Option<&'a Value>,
38 ref_context: Context<'a>,
39) -> ErrorIterator<'a>;
40
41pub fn descend<'a>(
45 cfg: &'a Config<'a>,
46 instance: &'a Value,
47 schema: &'a Value,
48 _parent_schema: Option<&'a Value>,
49 ref_context: Context<'a>,
50) -> ErrorIterator<'a> {
51 match schema {
52 Bool(b) => {
53 if *b {
54 no_error()
55 } else {
56 make_error("false schema always fails", Some(instance), Some(schema))
57 }
58 }
59 Object(schema_object) => {
60 if let (Some(ref_), Some(validator)) =
61 (schema_object.get("$ref"), cfg.get_validator("$ref"))
62 {
63 Box::new(validator(cfg, instance, ref_, Some(schema), ref_context))
64 } else {
65 Box::new(
66 schema_object
67 .iter()
68 .flat_map(move |(k, v)| -> ErrorIterator<'a> {
69 if let Some(validator) = cfg.get_validator(k) {
70 Box::new(
71 validator(cfg, instance, v, Some(schema), ref_context)
72 .map(move |err| err.schema_ctx(k.to_string())),
73 )
74 } else {
75 no_error()
76 }
77 }),
78 )
79 }
80 }
81 _ => make_error(
82 "Invalid schema. Must be boolean or object.",
83 None,
84 Some(schema),
85 ),
86 }
87}
88
89pub fn patternProperties<'a>(
93 cfg: &'a Config<'a>,
94 instance: &'a Value,
95 schema: &'a Value,
96 _parent_schema: Option<&'a Value>,
97 ref_context: Context<'a>,
98) -> ErrorIterator<'a> {
99 if let (Object(instance_object), Object(schema_object)) = (instance, schema) {
100 Box::new(schema_object.iter().flat_map(move |(pattern, subschema)| {
101 if let Ok(re) = regex::Regex::new(pattern) {
102 Box::new(
103 instance_object
104 .iter()
105 .flat_map(move |(k, v)| {
106 if re.is_match(k) {
107 Box::new(
108 descend(cfg, v, subschema, Some(schema), ref_context)
109 .map(move |err| err.instance_ctx(k.clone())),
110 )
111 } else {
112 no_error()
113 }
114 })
115 .map(move |err| err.schema_ctx(pattern.clone())),
116 )
117 } else {
118 no_error()
119 }
120 }))
121 } else {
122 no_error()
123 }
124}
125
126pub fn propertyNames<'a>(
127 cfg: &'a Config<'a>,
128 instance: &'a Value,
129 schema: &'a Value,
130 parent_schema: Option<&'a Value>,
131 ref_context: Context<'a>,
132) -> ErrorIterator<'a> {
133 struct PropertyNameIter<'a> {
134 instance_cursor: Box<dyn Iterator<Item = &'a String> + 'a>,
135 cfg: &'a Config<'a>,
136 schema: &'a Value,
137 parent_schema: Option<&'a Value>,
138 ref_context: Context<'a>,
139 collected_errors: Vec<ValidationError>,
140 error_i: usize,
141 }
142
143 impl<'a> Iterator for PropertyNameIter<'a> {
144 type Item = ValidationError;
145
146 fn next(&mut self) -> Option<Self::Item> {
147 loop {
148 if self.error_i < self.collected_errors.len() {
149 self.error_i += 1;
150 return Some(self.collected_errors[self.error_i - 1].clone());
151 } else if let Some(instance) = self.instance_cursor.next() {
152 let key = Value::String(instance.to_string());
153 self.collected_errors = descend(
154 self.cfg,
155 &key,
156 self.schema,
157 self.parent_schema,
158 self.ref_context,
159 )
160 .collect();
161 self.error_i = 0;
162 } else {
163 return None;
164 }
165 }
166 }
167 }
168
169 if let Object(instance) = instance {
170 Box::new(PropertyNameIter {
171 instance_cursor: Box::new(instance.keys()),
172 cfg,
173 schema,
174 parent_schema,
175 ref_context,
176 collected_errors: Vec::new(),
177 error_i: 0,
178 })
179 } else {
180 no_error()
181 }
182}
183
184fn find_additional_properties<'a>(
185 instance: &'a Map<String, Value>,
186 schema: &'a Map<String, Value>,
187) -> Box<dyn Iterator<Item = &'a str> + 'a> {
188 let properties = schema.get("properties").and_then(Value::as_object);
189 let pattern_regexes = schema
190 .get("patternProperties")
191 .and_then(Value::as_object)
192 .map(|x| {
193 x.keys()
194 .filter_map(|k| regex::Regex::new(k).ok())
195 .collect::<Vec<regex::Regex>>()
196 });
197 Box::new(
198 instance
199 .keys()
200 .filter(move |&property| {
201 !properties.map_or_else(|| false, |x| x.contains_key(property))
202 })
203 .filter(move |&property| {
204 !pattern_regexes
205 .as_ref()
206 .map_or_else(|| false, |x| x.iter().any(|y| y.is_match(property)))
207 })
208 .map(|x| x.as_str()),
209 )
210}
211
212pub fn additionalProperties<'a>(
213 cfg: &'a Config<'a>,
214 instance: &'a Value,
215 schema: &'a Value,
216 parent_schema: Option<&'a Value>,
217 ref_context: Context<'a>,
218) -> ErrorIterator<'a> {
219 if let Object(instance_map) = instance {
220 let extras = parent_schema
221 .and_then(|x| x.as_object())
222 .map(|x| find_additional_properties(instance_map, x));
223
224 if let Some(mut extras) = extras {
225 match schema {
226 Object(_) => {
227 return Box::new(extras.flat_map(move |extra| {
228 Box::new(
229 descend(
230 cfg,
231 instance.get(extra).unwrap(),
232 schema,
233 parent_schema,
234 ref_context,
235 )
236 .map(move |err| err.instance_ctx(extra.to_string())),
237 )
238 }));
239 }
240 Bool(bool) => {
241 if !bool {
242 let extra_string = util::format_list(&mut extras);
243 if !extra_string.is_empty() {
244 return make_error(
245 format!(
246 "Additional properties are not allowed. Found {}.",
247 extra_string
248 ),
249 Some(instance),
250 parent_schema,
251 );
252 }
253 }
254 }
255 _ => {}
256 }
257 }
258 }
259 no_error()
260}
261
262pub fn items<'a>(
263 cfg: &'a Config<'a>,
264 instance: &'a Value,
265 schema: &'a Value,
266 _parent_schema: Option<&'a Value>,
267 ref_context: Context<'a>,
268) -> ErrorIterator<'a> {
269 if let Array(instance) = instance {
270 let items = if cfg.get_draft_number() >= 6 {
271 util::bool_to_object_schema(schema)
272 } else {
273 schema
274 };
275
276 match items {
277 Object(_) => Box::new(instance.iter().enumerate().flat_map(move |(index, item)| {
278 Box::new(
279 descend(cfg, item, items, Some(schema), ref_context)
280 .map(move |err| err.instance_ctx(index.to_string())),
281 )
282 })),
283 Array(items) => Box::new(instance.iter().enumerate().zip(items.iter()).flat_map(
284 move |((index, item), subschema)| {
285 Box::new(
286 descend(cfg, item, subschema, Some(schema), ref_context)
287 .map(move |err| err.add_ctx(index.to_string(), index.to_string())),
288 )
289 },
290 )),
291 _ => no_error(),
292 }
293 } else {
294 no_error()
295 }
296}
297
298pub fn additionalItems<'a>(
299 cfg: &'a Config<'a>,
300 instance: &'a Value,
301 schema: &'a Value,
302 parent_schema: Option<&'a Value>,
303 ref_context: Context<'a>,
304) -> ErrorIterator<'a> {
305 if let Some(parent_schema) = parent_schema {
306 if let (Array(instance_array), Some(Array(items))) = (instance, parent_schema.get("items"))
307 {
308 match schema {
309 Object(_) => {
310 return Box::new(
311 instance_array
312 .iter()
313 .enumerate()
314 .skip(items.len())
315 .flat_map(move |(index, item)| {
316 Box::new(
317 descend(cfg, item, schema, Some(parent_schema), ref_context)
318 .map(move |err| err.instance_ctx(index.to_string())),
319 )
320 }),
321 )
322 }
323 Bool(b) => {
324 if !b && instance_array.len() > items.len() {
325 return make_error(
326 "Additional items are not allowed.",
327 Some(instance),
328 Some(parent_schema),
329 );
330 }
331 }
332 _ => {}
333 }
334 }
335 }
336 no_error()
337}
338
339pub fn const_<'a>(
340 _cfg: &'a Config<'a>,
341 instance: &'a Value,
342 schema: &'a Value,
343 _parent_schema: Option<&'a Value>,
344 _ref_context: Context<'a>,
345) -> ErrorIterator<'a> {
346 if !util::json_equal(instance, schema) {
347 make_error("const doesn't match.", Some(instance), Some(schema))
348 } else {
349 no_error()
350 }
351}
352
353pub fn contains<'a>(
354 cfg: &'a Config<'a>,
355 instance: &'a Value,
356 schema: &'a Value,
357 parent_schema: Option<&'a Value>,
358 ref_context: Context<'a>,
359) -> ErrorIterator<'a> {
360 if let Array(instance_array) = instance {
361 for item in instance_array {
362 if descend(cfg, item, schema, parent_schema, ref_context)
363 .next()
364 .is_none()
365 {
366 return no_error();
367 }
368 }
369 return make_error(
370 "No items in array valid under the given schema.",
371 Some(instance),
372 Some(schema),
373 );
374 }
375 no_error()
376}
377
378pub fn exclusiveMinimum<'a>(
379 _cfg: &'a Config<'a>,
380 instance: &'a Value,
381 schema: &'a Value,
382 _parent_schema: Option<&'a Value>,
383 _ref_context: Context<'a>,
384) -> ErrorIterator<'a> {
385 if let (Value::Number(instance_number), Value::Number(schema_number)) = (instance, schema) {
386 if instance_number.as_f64() <= schema_number.as_f64() {
387 return make_error(
388 format!("{} <= exclusiveMinimum {}", instance_number, schema_number),
389 Some(instance),
390 Some(schema),
391 );
392 }
393 }
394 no_error()
395}
396
397pub fn exclusiveMaximum<'a>(
398 _cfg: &'a Config<'a>,
399 instance: &'a Value,
400 schema: &'a Value,
401 _parent_schema: Option<&'a Value>,
402 _ref_context: Context<'a>,
403) -> ErrorIterator<'a> {
404 if let (Value::Number(instance_number), Value::Number(schema_number)) = (instance, schema) {
405 if instance_number.as_f64() >= schema_number.as_f64() {
406 return make_error(
407 format!("{} >= exclusiveMaximum {}", instance_number, schema_number),
408 Some(instance),
409 Some(schema),
410 );
411 }
412 }
413 no_error()
414}
415
416pub fn minimum_draft4<'a>(
417 _cfg: &'a Config<'a>,
418 instance: &'a Value,
419 schema: &'a Value,
420 parent_schema: Option<&'a Value>,
421 _ref_context: Context<'a>,
422) -> ErrorIterator<'a> {
423 if let (Value::Number(instance_number), Value::Number(minimum)) = (instance, schema) {
424 if parent_schema
425 .and_then(|x| x.get("exclusiveMinimum"))
426 .and_then(Value::as_bool)
427 .unwrap_or(false)
428 {
429 if instance_number.as_f64() <= minimum.as_f64() {
430 return make_error(
431 format!("{} <= exclusiveMinimum {}", instance_number, minimum),
432 Some(instance),
433 Some(schema),
434 );
435 }
436 } else if instance_number.as_f64() < minimum.as_f64() {
437 return make_error(
438 format!("{} <= minimum {}", instance_number, minimum),
439 Some(instance),
440 Some(schema),
441 );
442 }
443 }
444 no_error()
445}
446
447pub fn minimum<'a>(
448 _cfg: &'a Config<'a>,
449 instance: &'a Value,
450 schema: &'a Value,
451 _parent_schema: Option<&'a Value>,
452 _ref_context: Context<'a>,
453) -> ErrorIterator<'a> {
454 if let (Value::Number(instance_number), Value::Number(schema_number)) = (instance, schema) {
455 if instance.as_f64() < schema_number.as_f64() {
456 return make_error(
457 format!("{} < minimum {}", instance_number, schema_number),
458 Some(instance),
459 Some(schema),
460 );
461 }
462 }
463 no_error()
464}
465
466pub fn maximum_draft4<'a>(
467 _cfg: &'a Config<'a>,
468 instance: &'a Value,
469 schema: &'a Value,
470 parent_schema: Option<&'a Value>,
471 _ref_context: Context<'a>,
472) -> ErrorIterator<'a> {
473 if let (Value::Number(instance_number), Value::Number(maximum)) = (instance, schema) {
474 if parent_schema
475 .and_then(|x| x.get("exclusiveMaximum"))
476 .and_then(Value::as_bool)
477 .unwrap_or(false)
478 {
479 if instance_number.as_f64() >= maximum.as_f64() {
480 return make_error(
481 format!("{} >= exclusiveMaximum {}", instance_number, maximum),
482 Some(instance),
483 Some(schema),
484 );
485 }
486 } else if instance_number.as_f64() > maximum.as_f64() {
487 return make_error(
488 format!("{} > maximum {}", instance_number, maximum),
489 Some(instance),
490 Some(schema),
491 );
492 }
493 }
494 no_error()
495}
496
497pub fn maximum<'a>(
498 _cfg: &'a Config<'a>,
499 instance: &'a Value,
500 schema: &'a Value,
501 _parent_schema: Option<&'a Value>,
502 _ref_context: Context<'a>,
503) -> ErrorIterator<'a> {
504 if let (Value::Number(instance_number), Value::Number(maximum)) = (instance, schema) {
505 if instance_number.as_f64() > maximum.as_f64() {
506 return make_error(
507 format!("{} > maximum {}", instance_number, maximum),
508 Some(instance),
509 Some(schema),
510 );
511 }
512 }
513 no_error()
514}
515
516#[allow(clippy::float_cmp)]
517pub fn multipleOf<'a>(
518 _cfg: &'a Config<'a>,
519 instance: &'a Value,
520 schema: &'a Value,
521 _parent_schema: Option<&'a Value>,
522 _ref_context: Context<'a>,
523) -> ErrorIterator<'a> {
524 if let (Value::Number(instance_number), Value::Number(schema_number)) = (instance, schema) {
525 let failed = if schema_number.is_f64() {
526 let quotient = instance_number.as_f64().unwrap() / schema_number.as_f64().unwrap();
527 quotient.trunc() != quotient
528 } else if schema_number.is_u64() {
529 (instance_number.as_u64().unwrap() % schema_number.as_u64().unwrap()) != 0
530 } else {
531 (instance_number.as_i64().unwrap() % schema_number.as_i64().unwrap()) != 0
532 };
533 if failed {
534 return make_error(
535 format!("{} not multipleOf {}", instance_number, schema_number),
536 Some(instance),
537 Some(schema),
538 );
539 }
540 }
541 no_error()
542}
543
544pub fn minItems<'a>(
545 _cfg: &'a Config<'a>,
546 instance: &'a Value,
547 schema: &'a Value,
548 _parent_schema: Option<&'a Value>,
549 _ref_context: Context<'a>,
550) -> ErrorIterator<'a> {
551 if let (Array(instance_array), Value::Number(schema_number)) = (instance, schema) {
552 if instance_array.len() < schema_number.as_u64().unwrap() as usize {
553 return make_error(
554 format!("{} < minItems {}", instance_array.len(), schema_number),
555 Some(instance),
556 Some(schema),
557 );
558 }
559 }
560 no_error()
561}
562
563pub fn maxItems<'a>(
564 _cfg: &'a Config<'a>,
565 instance: &'a Value,
566 schema: &'a Value,
567 _parent_schema: Option<&'a Value>,
568 _ref_context: Context<'a>,
569) -> ErrorIterator<'a> {
570 if let (Array(instance_array), Value::Number(schema_number)) = (instance, schema) {
571 if instance_array.len() > schema_number.as_u64().unwrap() as usize {
572 return make_error(
573 format!("{} > maxItems {}", instance_array.len(), schema_number),
574 Some(instance),
575 Some(schema),
576 );
577 }
578 }
579 no_error()
580}
581
582pub fn uniqueItems<'a>(
583 _cfg: &'a Config<'a>,
584 instance: &'a Value,
585 schema: &'a Value,
586 _parent_schema: Option<&'a Value>,
587 _ref_context: Context<'a>,
588) -> ErrorIterator<'a> {
589 if let (Array(instance_array), Bool(schema)) = (instance, schema) {
590 if *schema && !unique::has_unique_elements(&mut instance_array.iter()) {
591 return make_error("Items are not unique", Some(instance), None);
592 }
593 }
594 no_error()
595}
596
597pub fn pattern<'a>(
598 _cfg: &'a Config<'a>,
599 instance: &'a Value,
600 schema: &'a Value,
601 _parent_schema: Option<&'a Value>,
602 _ref_context: Context<'a>,
603) -> ErrorIterator<'a> {
604 if let (Value::String(instance_string), Value::String(schema_string)) = (instance, schema) {
605 if let Ok(re) = regex::Regex::new(schema_string) {
606 if !re.is_match(instance_string) {
607 return make_error("Does not match pattern.", Some(instance), Some(schema));
608 }
609 } else {
610 return make_error("Invalid regex.", None, Some(schema));
611 }
612 }
613 no_error()
614}
615
616pub fn format<'a>(
617 cfg: &'a Config<'a>,
618 instance: &'a Value,
619 schema: &'a Value,
620 _parent_schema: Option<&'a Value>,
621 _ref_context: Context<'a>,
622) -> ErrorIterator<'a> {
623 if let (Value::String(instance_string), Value::String(schema_string)) = (instance, schema) {
624 if let Some(checker) = cfg.get_format_checker(schema_string) {
625 if !checker(cfg, instance_string) {
626 return make_error("Invalid for format.", Some(instance), Some(schema));
627 }
628 }
629 }
630 no_error()
631}
632
633pub fn minLength<'a>(
634 _cfg: &'a Config<'a>,
635 instance: &'a Value,
636 schema: &'a Value,
637 _parent_schema: Option<&'a Value>,
638 _ref_context: Context<'a>,
639) -> ErrorIterator<'a> {
640 if let (Value::String(instance_string), Value::Number(schema_number)) = (instance, schema) {
641 let count = instance_string.chars().count();
642 if count < schema_number.as_u64().unwrap() as usize {
643 return make_error(
644 format!("{} < minLength {}", count, schema_number),
645 Some(instance),
646 Some(schema),
647 );
648 }
649 }
650 no_error()
651}
652
653pub fn maxLength<'a>(
654 _cfg: &'a Config<'a>,
655 instance: &'a Value,
656 schema: &'a Value,
657 _parent_schema: Option<&'a Value>,
658 _ref_context: Context<'a>,
659) -> ErrorIterator<'a> {
660 if let (Value::String(instance_string), Value::Number(schema_number)) = (instance, schema) {
661 let count = instance_string.chars().count();
662 if count > schema_number.as_u64().unwrap() as usize {
663 return make_error(
664 format!("{} < maxLength {}", count, schema_number),
665 Some(instance),
666 Some(schema),
667 );
668 }
669 }
670 no_error()
671}
672
673pub fn dependencies<'a>(
674 cfg: &'a Config<'a>,
675 instance: &'a Value,
676 schema: &'a Value,
677 _parent_schema: Option<&'a Value>,
678 ref_context: Context<'a>,
679) -> ErrorIterator<'a> {
680 if let (Object(instance_object), Object(schema_object)) = (instance, schema) {
681 Box::new(
682 schema_object
683 .iter()
684 .filter(move |(property, _dependency)| {
685 instance_object.contains_key(property.as_str())
686 })
687 .flat_map(move |(property, dependency)| -> ErrorIterator<'a> {
688 let dep = util::bool_to_object_schema(dependency);
689 if let Object(_) = dep {
690 return Box::new(
691 descend(cfg, instance, dep, Some(schema), ref_context)
692 .map(move |err| err.schema_ctx(property.clone())),
693 );
694 } else {
695 for dep0 in util::iter_or_once(dep) {
696 if let Value::String(key) = dep0 {
697 if !instance_object.contains_key(key) {
698 return make_error(
699 "Invalid dependencies",
700 Some(instance),
701 Some(schema),
702 );
703 }
704 }
705 }
706 }
707 no_error()
708 }),
709 )
710 } else {
711 no_error()
712 }
713}
714
715pub fn enum_<'a>(
716 _cfg: &'a Config<'a>,
717 instance: &'a Value,
718 schema: &'a Value,
719 _parent_schema: Option<&'a Value>,
720 _ref_context: Context<'a>,
721) -> ErrorIterator<'a> {
722 if let Array(enums) = schema {
723 if !enums.iter().any(|val| util::json_equal(val, instance)) {
724 return make_error("Value is not in enum.", Some(instance), Some(schema));
725 }
726 }
727 no_error()
728}
729
730#[allow(clippy::float_cmp)]
731fn single_type(instance: &Value, schema: &Value) -> bool {
732 if let Value::String(typename) = schema {
733 return match typename.as_ref() {
734 "array" => matches!(instance, Array(_)),
735 "object" => matches!(instance, Object(_)),
736 "null" => matches!(instance, Value::Null),
737 "number" => matches!(instance, Value::Number(_)),
738 "string" => matches!(instance, Value::String(_)),
739 "integer" => {
740 if let Value::Number(number) = instance {
741 number.is_i64()
742 || number.is_u64()
743 || (number.is_f64()
744 && number.as_f64().unwrap().trunc() == number.as_f64().unwrap())
745 } else {
746 false
747 }
748 }
749 "boolean" => matches!(instance, Bool(_)),
750 _ => true,
751 };
752 }
753 true
754}
755
756pub fn type_<'a>(
757 _cfg: &'a Config<'a>,
758 instance: &'a Value,
759 schema: &'a Value,
760 parent_schema: Option<&'a Value>,
761 _ref_context: Context<'a>,
762) -> ErrorIterator<'a> {
763 if !util::iter_or_once(schema).any(|x| single_type(instance, x)) {
764 return make_error("Invalid type.", Some(instance), parent_schema);
765 }
766 no_error()
767}
768
769pub fn properties<'a>(
770 cfg: &'a Config<'a>,
771 instance: &'a Value,
772 schema: &'a Value,
773 _parent_schema: Option<&'a Value>,
774 ref_context: Context<'a>,
775) -> ErrorIterator<'a> {
776 if let (Object(instance_object), Object(schema_object)) = (instance, schema) {
777 Box::new(schema_object.iter().flat_map(move |(property, subschema)| {
778 if let Some(property_value) = instance_object.get(property) {
779 Box::new(
780 descend(cfg, property_value, subschema, Some(schema), ref_context)
781 .map(move |err| err.add_ctx(property.clone(), property.clone())),
782 )
783 } else {
784 no_error()
785 }
786 }))
787 } else {
788 no_error()
789 }
790}
791
792pub fn required<'a>(
793 _cfg: &'a Config<'a>,
794 instance: &'a Value,
795 schema: &'a Value,
796 _parent_schema: Option<&'a Value>,
797 _ref_context: Context<'a>,
798) -> ErrorIterator<'a> {
799 if let (Object(instance_object), Array(schema_array)) = (instance, schema) {
800 let missing_properties: Vec<&str> = schema_array
801 .iter()
802 .filter_map(Value::as_str)
803 .filter(|&x| !instance_object.contains_key(&x.to_string()))
804 .collect();
805
806 if !missing_properties.is_empty() {
807 return make_error(
808 format!(
809 "Required properties {} are missing",
810 util::format_list(&mut missing_properties.iter().copied())
811 ),
812 Some(instance),
813 Some(schema),
814 );
815 }
816 }
817 no_error()
818}
819
820pub fn minProperties<'a>(
821 _cfg: &'a Config<'a>,
822 instance: &'a Value,
823 schema: &'a Value,
824 _parent_schema: Option<&'a Value>,
825 _ref_context: Context<'a>,
826) -> ErrorIterator<'a> {
827 if let (Object(instance_object), Value::Number(schema_number)) = (instance, schema) {
828 if instance_object.len() < schema_number.as_u64().unwrap() as usize {
829 return make_error(
830 format!(
831 "{} < minProperties {}",
832 instance_object.len(),
833 schema_number
834 ),
835 Some(instance),
836 Some(schema),
837 );
838 }
839 }
840 no_error()
841}
842
843pub fn maxProperties<'a>(
844 _cfg: &'a Config<'a>,
845 instance: &'a Value,
846 schema: &'a Value,
847 _parent_schema: Option<&'a Value>,
848 _ref_context: Context<'a>,
849) -> ErrorIterator<'a> {
850 if let (Object(instance_object), Value::Number(schema_number)) = (instance, schema) {
851 if instance_object.len() > schema_number.as_u64().unwrap() as usize {
852 return make_error(
853 format!(
854 "{} > maxProperties {}",
855 instance_object.len(),
856 schema_number
857 ),
858 Some(instance),
859 Some(schema),
860 );
861 }
862 }
863 no_error()
864}
865
866pub fn allOf<'a>(
867 cfg: &'a Config<'a>,
868 instance: &'a Value,
869 schema: &'a Value,
870 _parent_schema: Option<&'a Value>,
871 ref_context: Context<'a>,
872) -> ErrorIterator<'a> {
873 if let Array(schema_array) = schema {
874 Box::new(
875 schema_array
876 .iter()
877 .enumerate()
878 .flat_map(move |(index, subschema)| {
879 let subschema0 = if cfg.get_draft_number() >= 6 {
880 util::bool_to_object_schema(subschema)
881 } else {
882 subschema
883 };
884 Box::new(
885 descend(cfg, instance, subschema0, Some(schema), ref_context)
886 .map(move |err| err.schema_ctx(index.to_string())),
887 )
888 }),
889 )
890 } else {
891 no_error()
892 }
893}
894
895pub fn anyOf<'a>(
896 cfg: &'a Config<'a>,
897 instance: &'a Value,
898 schema: &'a Value,
899 _parent_schema: Option<&'a Value>,
900 ref_context: Context<'a>,
901) -> ErrorIterator<'a> {
902 if let Array(schema_array) = schema {
903 for subschema in schema_array.iter() {
904 let subschema0 = if cfg.get_draft_number() >= 6 {
905 util::bool_to_object_schema(subschema)
906 } else {
907 subschema
908 };
909 if descend(cfg, instance, subschema0, Some(schema), ref_context)
910 .next()
911 .is_none()
912 {
913 return no_error();
914 }
915 }
916 return make_error("anyOf failed", Some(instance), Some(schema));
917 }
918 no_error()
919}
920
921pub fn oneOf<'a>(
922 cfg: &'a Config<'a>,
923 instance: &'a Value,
924 schema: &'a Value,
925 _parent_schema: Option<&'a Value>,
926 ref_context: Context<'a>,
927) -> ErrorIterator<'a> {
928 if let Array(schema_array) = schema {
929 let mut oneOf = schema_array.iter().enumerate();
930 let mut found_one = false;
931 for (_, subschema) in oneOf.by_ref() {
932 let subschema0 = if cfg.get_draft_number() >= 6 {
933 util::bool_to_object_schema(subschema)
934 } else {
935 subschema
936 };
937 if descend(cfg, instance, subschema0, Some(schema), ref_context)
938 .next()
939 .is_none()
940 {
941 found_one = true;
942 break;
943 }
944 }
945
946 if !found_one {
947 return make_error("nothing matched in oneOf", Some(instance), Some(schema));
948 }
949
950 let mut found_more = false;
951 for (_, subschema) in oneOf.by_ref() {
952 let subschema0 = if cfg.get_draft_number() >= 6 {
953 util::bool_to_object_schema(subschema)
954 } else {
955 subschema
956 };
957 if descend(cfg, instance, subschema0, Some(schema), ref_context)
958 .next()
959 .is_none()
960 {
961 found_more = true;
962 break;
963 }
964 }
965
966 if found_more {
967 return make_error(
968 "More than one matched in oneOf",
969 Some(instance),
970 Some(schema),
971 );
972 }
973 }
974 no_error()
975}
976
977pub fn not<'a>(
978 cfg: &'a Config<'a>,
979 instance: &'a Value,
980 schema: &'a Value,
981 parent_schema: Option<&'a Value>,
982 ref_context: Context<'a>,
983) -> ErrorIterator<'a> {
984 if descend(cfg, instance, schema, parent_schema, ref_context)
985 .next()
986 .is_none()
987 {
988 make_error("not", Some(instance), Some(schema))
989 } else {
990 no_error()
991 }
992}
993
994pub fn ref_<'a>(
995 cfg: &'a Config<'a>,
996 instance: &'a Value,
997 schema: &'a Value,
998 _parent_schema: Option<&'a Value>,
999 ref_context: Context<'a>,
1000) -> ErrorIterator<'a> {
1001 if let Value::String(sref) = schema {
1002 struct RefIter {
1003 collected_errors: Vec<ValidationError>,
1004 error_i: usize,
1005 }
1006
1007 impl Iterator for RefIter {
1008 type Item = ValidationError;
1009
1010 fn next(&mut self) -> Option<Self::Item> {
1011 if self.error_i < self.collected_errors.len() {
1012 self.error_i += 1;
1013 Some(self.collected_errors[self.error_i - 1].clone())
1014 } else {
1015 None
1016 }
1017 }
1018 }
1019
1020 match cfg
1021 .get_resolver()
1022 .resolve_fragment(cfg.draft, sref, &ref_context, cfg.get_schema())
1023 {
1024 Ok((scope, resolved)) => {
1025 let scope_schema = json!({"$id": scope.to_string()});
1026 return Box::new(RefIter {
1027 collected_errors: descend(
1028 cfg,
1029 instance,
1030 resolved,
1031 Some(schema),
1032 ref_context.push(&scope_schema),
1033 )
1034 .collect(),
1035 error_i: 0,
1036 });
1037 }
1038 Err(_err) => {
1039 return make_error(
1040 format!("Couldn't resolve reference {}", sref),
1041 Some(instance),
1042 None,
1043 )
1044 }
1045 }
1046 }
1047 no_error()
1048}
1049
1050pub fn if_<'a>(
1051 cfg: &'a Config<'a>,
1052 instance: &'a Value,
1053 schema: &'a Value,
1054 parent_schema: Option<&'a Value>,
1055 ref_context: Context<'a>,
1056) -> ErrorIterator<'a> {
1057 if descend(cfg, instance, schema, parent_schema, ref_context)
1058 .next()
1059 .is_none()
1060 {
1061 if let Some(then) = parent_schema.and_then(|x| x.get("then")) {
1062 if then.is_object() {
1063 return Box::new(
1064 descend(cfg, instance, then, Some(schema), ref_context)
1065 .map(move |err| err.schema_ctx("then".to_string())),
1066 );
1067 }
1068 }
1069 } else if let Some(else_) = parent_schema.and_then(|x| x.get("else")) {
1070 if else_.is_object() {
1071 return Box::new(
1072 descend(cfg, instance, else_, Some(schema), ref_context)
1073 .map(move |err| err.schema_ctx("else".to_string())),
1074 );
1075 }
1076 }
1077 no_error()
1078}
1079
1080#[cfg(test)]
1081mod tests {
1082 use crate::{schemas, Config};
1083 use serde_json::json;
1084
1085 #[test]
1086 fn test_additional_properties_errors() {
1087 let schema = json!({
1088 "properties": { "foo": { "type": "integer" } },
1089 "additionalProperties": false
1090 });
1091 let instance = json!({
1092 "foo": 42,
1093 "bar": "additional",
1094 "baz": "another additional"
1095 });
1096 let cfg = Config::from_schema(&schema, Some(schemas::Draft::Draft6)).unwrap();
1097 let validation = cfg.validate(&instance);
1098
1099 if let Err(errors) = validation {
1100 for error in errors {
1101 let formatted = format!("{}", error);
1102 println!("{}", formatted);
1103
1104 assert!(error.instance_path == (Vec::<String>::new()));
1105 assert!(error.schema_path == vec!("additionalProperties"));
1106
1107 assert!(formatted
1108 .contains("Additional properties are not allowed. Found \"bar\", \"baz\"."));
1109 assert!(formatted.contains("At instance path /:"));
1110 assert!(formatted.contains("At schema path /additionalProperties"));
1111 }
1112 }
1113 }
1114}