1use crate::{
39 paths::{LazyLocation, Location},
40 types::{JsonType, JsonTypeSet},
41 validator::LazyEvaluationPath,
42};
43use referencing::Uri;
44use serde_json::{Map, Number, Value};
45use std::{
46 borrow::Cow,
47 error,
48 fmt::{self, Formatter, Write},
49 iter::{empty, once},
50 slice,
51 string::FromUtf8Error,
52 sync::Arc,
53 vec,
54};
55
56#[derive(Debug)]
58pub struct ValidationError<'a> {
59 repr: Box<ValidationErrorRepr<'a>>,
60}
61
62struct ValidationErrorRepr<'a> {
63 instance: Cow<'a, Value>,
64 kind: ValidationErrorKind,
65 instance_path: Location,
66 schema_path: Location,
68 tracker: LazyEvaluationPath,
70 absolute_keyword_location: Option<Arc<Uri<String>>>,
75}
76
77impl fmt::Debug for ValidationErrorRepr<'_> {
78 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
79 f.debug_struct("ValidationErrorRepr")
80 .field("instance", &self.instance)
81 .field("kind", &self.kind)
82 .field("instance_path", &self.instance_path)
83 .field("schema_path", &self.schema_path)
84 .field("absolute_keyword_location", &self.absolute_keyword_location)
85 .finish_non_exhaustive()
86 }
87}
88
89#[doc(hidden)]
107pub trait ValidationErrorIterator<'a>: Iterator<Item = ValidationError<'a>> + Send + Sync {}
108
109impl<'a, T> ValidationErrorIterator<'a> for T where
110 T: Iterator<Item = ValidationError<'a>> + Send + Sync
111{
112}
113
114pub struct ErrorIterator<'a> {
119 iter: Box<dyn ValidationErrorIterator<'a> + 'a>,
120}
121
122impl<'a> ErrorIterator<'a> {
123 #[inline]
124 pub(crate) fn from_iterator<T>(iterator: T) -> Self
125 where
126 T: ValidationErrorIterator<'a> + 'a,
127 {
128 Self {
129 iter: Box::new(iterator),
130 }
131 }
132
133 #[inline]
135 #[must_use]
136 pub fn into_errors(self) -> ValidationErrors<'a> {
137 ValidationErrors {
138 errors: self.collect(),
139 }
140 }
141}
142
143pub struct ValidationErrors<'a> {
147 errors: Vec<ValidationError<'a>>,
148}
149
150impl<'a> ValidationErrors<'a> {
151 #[inline]
152 #[must_use]
153 pub fn len(&self) -> usize {
154 self.errors.len()
155 }
156
157 #[inline]
158 #[must_use]
159 pub fn is_empty(&self) -> bool {
160 self.errors.is_empty()
161 }
162
163 #[inline]
165 #[must_use]
166 pub fn as_slice(&self) -> &[ValidationError<'a>] {
167 &self.errors
168 }
169
170 #[inline]
171 pub fn iter(&self) -> slice::Iter<'_, ValidationError<'a>> {
172 self.errors.iter()
173 }
174
175 #[inline]
176 pub fn iter_mut(&mut self) -> slice::IterMut<'_, ValidationError<'a>> {
177 self.errors.iter_mut()
178 }
179}
180
181impl fmt::Display for ValidationErrors<'_> {
182 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
183 if self.errors.is_empty() {
184 f.write_str("Validation succeeded")
185 } else {
186 writeln!(f, "Validation errors:")?;
187 for (idx, error) in self.errors.iter().enumerate() {
188 writeln!(f, "{:02}: {error}", idx + 1)?;
189 }
190 Ok(())
191 }
192 }
193}
194
195impl fmt::Debug for ValidationErrors<'_> {
196 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
197 f.debug_struct("ValidationErrors")
198 .field("errors", &self.errors)
199 .finish()
200 }
201}
202
203impl error::Error for ValidationErrors<'_> {}
204
205impl<'a> Iterator for ErrorIterator<'a> {
206 type Item = ValidationError<'a>;
207
208 #[inline]
209 fn next(&mut self) -> Option<Self::Item> {
210 self.iter.as_mut().next()
211 }
212
213 #[inline]
214 fn size_hint(&self) -> (usize, Option<usize>) {
215 self.iter.size_hint()
216 }
217}
218
219impl<'a> IntoIterator for ValidationErrors<'a> {
220 type Item = ValidationError<'a>;
221 type IntoIter = vec::IntoIter<ValidationError<'a>>;
222
223 fn into_iter(self) -> Self::IntoIter {
224 self.errors.into_iter()
225 }
226}
227
228impl<'a, 'b> IntoIterator for &'b ValidationErrors<'a> {
229 type Item = &'b ValidationError<'a>;
230 type IntoIter = slice::Iter<'b, ValidationError<'a>>;
231
232 fn into_iter(self) -> Self::IntoIter {
233 self.errors.iter()
234 }
235}
236
237impl<'a, 'b> IntoIterator for &'b mut ValidationErrors<'a> {
238 type Item = &'b mut ValidationError<'a>;
239 type IntoIter = slice::IterMut<'b, ValidationError<'a>>;
240
241 fn into_iter(self) -> Self::IntoIter {
242 self.errors.iter_mut()
243 }
244}
245
246pub(crate) fn no_error<'a>() -> ErrorIterator<'a> {
248 ErrorIterator::from_iterator(empty())
249}
250pub(crate) fn error(instance: ValidationError) -> ErrorIterator {
252 ErrorIterator::from_iterator(once(instance))
253}
254
255#[derive(Debug)]
257#[allow(missing_docs)]
258pub enum ValidationErrorKind {
259 AdditionalItems { limit: usize },
261 AdditionalProperties { unexpected: Vec<String> },
263 AnyOf {
265 context: Vec<Vec<ValidationError<'static>>>,
266 },
267 BacktrackLimitExceeded { error: fancy_regex::Error },
269 Constant { expected_value: Value },
271 Contains,
273 ContentEncoding { content_encoding: String },
275 ContentMediaType { content_media_type: String },
277 Custom { keyword: String, message: String },
279 Enum { options: Value },
281 ExclusiveMaximum { limit: Value },
283 ExclusiveMinimum { limit: Value },
285 FalseSchema,
287 Format { format: String },
289 FromUtf8 { error: FromUtf8Error },
291 MaxItems { limit: u64 },
293 Maximum { limit: Value },
295 MaxLength { limit: u64 },
297 MaxProperties { limit: u64 },
299 MinItems { limit: u64 },
301 Minimum { limit: Value },
303 MinLength { limit: u64 },
305 MinProperties { limit: u64 },
307 MultipleOf {
309 #[cfg(feature = "arbitrary-precision")]
310 multiple_of: Value,
311 #[cfg(not(feature = "arbitrary-precision"))]
312 multiple_of: f64,
313 },
314 Not { schema: Value },
316 OneOfMultipleValid {
318 context: Vec<Vec<ValidationError<'static>>>,
319 },
320 OneOfNotValid {
322 context: Vec<Vec<ValidationError<'static>>>,
323 },
324 Pattern { pattern: String },
326 PropertyNames {
328 error: Box<ValidationError<'static>>,
329 },
330 Required { property: Value },
332 Type { kind: TypeKind },
334 UnevaluatedItems { unexpected: Vec<String> },
336 UnevaluatedProperties { unexpected: Vec<String> },
338 UniqueItems,
340 Referencing(referencing::Error),
342}
343
344impl ValidationErrorKind {
345 #[must_use]
346 pub fn keyword(&self) -> &str {
347 match self {
348 ValidationErrorKind::AdditionalItems { .. } => "additionalItems",
349 ValidationErrorKind::AdditionalProperties { .. } => "additionalProperties",
350 ValidationErrorKind::AnyOf { .. } => "anyOf",
351 ValidationErrorKind::BacktrackLimitExceeded { .. }
352 | ValidationErrorKind::Pattern { .. } => "pattern",
353 ValidationErrorKind::Constant { .. } => "const",
354 ValidationErrorKind::Contains => "contains",
355 ValidationErrorKind::ContentEncoding { .. } | ValidationErrorKind::FromUtf8 { .. } => {
356 "contentEncoding"
357 }
358 ValidationErrorKind::ContentMediaType { .. } => "contentMediaType",
359 ValidationErrorKind::Custom { keyword, .. } => keyword,
360 ValidationErrorKind::Enum { .. } => "enum",
361 ValidationErrorKind::ExclusiveMaximum { .. } => "exclusiveMaximum",
362 ValidationErrorKind::ExclusiveMinimum { .. } => "exclusiveMinimum",
363 ValidationErrorKind::FalseSchema => "falseSchema",
364 ValidationErrorKind::Format { .. } => "format",
365 ValidationErrorKind::MaxItems { .. } => "maxItems",
366 ValidationErrorKind::Maximum { .. } => "maximum",
367 ValidationErrorKind::MaxLength { .. } => "maxLength",
368 ValidationErrorKind::MaxProperties { .. } => "maxProperties",
369 ValidationErrorKind::MinItems { .. } => "minItems",
370 ValidationErrorKind::Minimum { .. } => "minimum",
371 ValidationErrorKind::MinLength { .. } => "minLength",
372 ValidationErrorKind::MinProperties { .. } => "minProperties",
373 ValidationErrorKind::MultipleOf { .. } => "multipleOf",
374 ValidationErrorKind::Not { .. } => "not",
375 ValidationErrorKind::OneOfMultipleValid { .. }
376 | ValidationErrorKind::OneOfNotValid { .. } => "oneOf",
377 ValidationErrorKind::PropertyNames { .. } => "propertyNames",
378 ValidationErrorKind::Required { .. } => "required",
379 ValidationErrorKind::Type { .. } => "type",
380 ValidationErrorKind::UnevaluatedItems { .. } => "unevaluatedItems",
381 ValidationErrorKind::UnevaluatedProperties { .. } => "unevaluatedProperties",
382 ValidationErrorKind::UniqueItems => "uniqueItems",
383 ValidationErrorKind::Referencing(_) => "$ref",
384 }
385 }
386}
387
388#[derive(Debug)]
389#[allow(missing_docs)]
390pub enum TypeKind {
391 Single(JsonType),
392 Multiple(JsonTypeSet),
393}
394
395#[derive(Debug)]
397#[non_exhaustive]
398pub struct ValidationErrorParts<'a> {
399 pub instance: Cow<'a, Value>,
400 pub kind: ValidationErrorKind,
401 pub instance_path: Location,
402 pub schema_path: Location,
403 pub evaluation_path: Location,
404 pub absolute_keyword_location: Option<Arc<Uri<String>>>,
405}
406
407impl<'a> ValidationError<'a> {
409 #[inline]
411 #[must_use]
412 pub(crate) fn new(
413 instance: Cow<'a, Value>,
414 kind: ValidationErrorKind,
415 instance_path: Location,
416 schema_path: Location,
417 tracker: impl Into<LazyEvaluationPath>,
418 ) -> Self {
419 Self {
420 repr: Box::new(ValidationErrorRepr {
421 instance,
422 kind,
423 instance_path,
424 schema_path,
425 tracker: tracker.into(),
426 absolute_keyword_location: None,
427 }),
428 }
429 }
430
431 #[inline]
433 #[must_use]
434 pub fn instance(&self) -> &Cow<'a, Value> {
435 &self.repr.instance
436 }
437
438 #[inline]
440 #[must_use]
441 pub fn kind(&self) -> &ValidationErrorKind {
442 &self.repr.kind
443 }
444
445 #[inline]
447 #[must_use]
448 pub fn instance_path(&self) -> &Location {
449 &self.repr.instance_path
450 }
451
452 #[inline]
457 #[must_use]
458 pub fn schema_path(&self) -> &Location {
459 &self.repr.schema_path
460 }
461
462 #[inline]
468 #[must_use]
469 pub fn evaluation_path(&self) -> &Location {
470 self.repr.tracker.resolve(&self.repr.schema_path)
471 }
472
473 #[inline]
481 #[must_use]
482 pub fn absolute_keyword_location(&self) -> Option<&Uri<String>> {
483 self.repr.absolute_keyword_location.as_deref()
484 }
485
486 #[inline]
490 pub(crate) fn with_absolute_keyword_location(mut self, uri: Option<Arc<Uri<String>>>) -> Self {
491 if self.repr.absolute_keyword_location.is_none() {
492 self.repr.absolute_keyword_location = uri;
493 }
494 self
495 }
496
497 #[inline]
499 #[must_use]
500 pub fn into_parts(self) -> ValidationErrorParts<'a> {
501 let repr = *self.repr;
502 let evaluation_path = repr.tracker.into_owned(repr.schema_path.clone());
503 ValidationErrorParts {
504 instance: repr.instance,
505 kind: repr.kind,
506 instance_path: repr.instance_path,
507 schema_path: repr.schema_path,
508 evaluation_path,
509 absolute_keyword_location: repr.absolute_keyword_location,
510 }
511 }
512
513 #[inline]
514 fn borrowed(
515 instance: &'a Value,
516 kind: ValidationErrorKind,
517 instance_path: Location,
518 schema_path: Location,
519 tracker: impl Into<LazyEvaluationPath>,
520 ) -> Self {
521 Self::new(
522 Cow::Borrowed(instance),
523 kind,
524 instance_path,
525 schema_path,
526 tracker,
527 )
528 }
529
530 #[must_use]
533 pub fn masked<'b>(&'b self) -> MaskedValidationError<'a, 'b, 'static> {
534 self.masked_with("value")
535 }
536
537 pub fn masked_with<'b, 'c>(
539 &'b self,
540 placeholder: impl Into<Cow<'c, str>>,
541 ) -> MaskedValidationError<'a, 'b, 'c> {
542 MaskedValidationError {
543 error: self,
544 placeholder: placeholder.into(),
545 }
546 }
547 #[must_use]
549 pub fn to_owned(self) -> ValidationError<'static> {
550 let parts = self.into_parts();
551 ValidationError::new(
552 Cow::Owned(parts.instance.into_owned()),
553 parts.kind,
554 parts.instance_path,
555 parts.schema_path,
556 parts.evaluation_path,
557 )
558 .with_absolute_keyword_location(parts.absolute_keyword_location)
559 }
560
561 pub(crate) fn additional_items(
562 schema_path: Location,
563 tracker: impl Into<LazyEvaluationPath>,
564 instance_path: Location,
565 instance: &'a Value,
566 limit: usize,
567 ) -> ValidationError<'a> {
568 Self::borrowed(
569 instance,
570 ValidationErrorKind::AdditionalItems { limit },
571 instance_path,
572 schema_path,
573 tracker,
574 )
575 }
576 pub(crate) fn additional_properties(
577 schema_path: Location,
578 tracker: impl Into<LazyEvaluationPath>,
579 instance_path: Location,
580 instance: &'a Value,
581 unexpected: Vec<String>,
582 ) -> ValidationError<'a> {
583 Self::borrowed(
584 instance,
585 ValidationErrorKind::AdditionalProperties { unexpected },
586 instance_path,
587 schema_path,
588 tracker,
589 )
590 }
591 pub(crate) fn any_of(
592 schema_path: Location,
593 tracker: impl Into<LazyEvaluationPath>,
594 instance_path: Location,
595 instance: &'a Value,
596 context: Vec<Vec<ValidationError<'a>>>,
597 ) -> ValidationError<'a> {
598 let context = context
599 .into_iter()
600 .map(|errors| errors.into_iter().map(ValidationError::to_owned).collect())
601 .collect::<Vec<_>>();
602
603 Self::borrowed(
604 instance,
605 ValidationErrorKind::AnyOf { context },
606 instance_path,
607 schema_path,
608 tracker,
609 )
610 }
611 pub(crate) fn backtrack_limit(
612 schema_path: Location,
613 tracker: impl Into<LazyEvaluationPath>,
614 instance_path: Location,
615 instance: &'a Value,
616 error: fancy_regex::Error,
617 ) -> ValidationError<'a> {
618 Self::borrowed(
619 instance,
620 ValidationErrorKind::BacktrackLimitExceeded { error },
621 instance_path,
622 schema_path,
623 tracker,
624 )
625 }
626 pub(crate) fn constant_array(
627 schema_path: Location,
628 tracker: impl Into<LazyEvaluationPath>,
629 instance_path: Location,
630 instance: &'a Value,
631 expected_value: &[Value],
632 ) -> ValidationError<'a> {
633 Self::borrowed(
634 instance,
635 ValidationErrorKind::Constant {
636 expected_value: Value::Array(expected_value.to_vec()),
637 },
638 instance_path,
639 schema_path,
640 tracker,
641 )
642 }
643 pub(crate) fn constant_boolean(
644 schema_path: Location,
645 tracker: impl Into<LazyEvaluationPath>,
646 instance_path: Location,
647 instance: &'a Value,
648 expected_value: bool,
649 ) -> ValidationError<'a> {
650 Self::borrowed(
651 instance,
652 ValidationErrorKind::Constant {
653 expected_value: Value::Bool(expected_value),
654 },
655 instance_path,
656 schema_path,
657 tracker,
658 )
659 }
660 pub(crate) fn constant_null(
661 schema_path: Location,
662 tracker: impl Into<LazyEvaluationPath>,
663 instance_path: Location,
664 instance: &'a Value,
665 ) -> ValidationError<'a> {
666 Self::borrowed(
667 instance,
668 ValidationErrorKind::Constant {
669 expected_value: Value::Null,
670 },
671 instance_path,
672 schema_path,
673 tracker,
674 )
675 }
676 pub(crate) fn constant_number(
677 schema_path: Location,
678 tracker: impl Into<LazyEvaluationPath>,
679 instance_path: Location,
680 instance: &'a Value,
681 expected_value: &Number,
682 ) -> ValidationError<'a> {
683 Self::borrowed(
684 instance,
685 ValidationErrorKind::Constant {
686 expected_value: Value::Number(expected_value.clone()),
687 },
688 instance_path,
689 schema_path,
690 tracker,
691 )
692 }
693 pub(crate) fn constant_object(
694 schema_path: Location,
695 tracker: impl Into<LazyEvaluationPath>,
696 instance_path: Location,
697 instance: &'a Value,
698 expected_value: &Map<String, Value>,
699 ) -> ValidationError<'a> {
700 Self::borrowed(
701 instance,
702 ValidationErrorKind::Constant {
703 expected_value: Value::Object(expected_value.clone()),
704 },
705 instance_path,
706 schema_path,
707 tracker,
708 )
709 }
710 pub(crate) fn constant_string(
711 schema_path: Location,
712 tracker: impl Into<LazyEvaluationPath>,
713 instance_path: Location,
714 instance: &'a Value,
715 expected_value: &str,
716 ) -> ValidationError<'a> {
717 Self::borrowed(
718 instance,
719 ValidationErrorKind::Constant {
720 expected_value: Value::String(expected_value.to_string()),
721 },
722 instance_path,
723 schema_path,
724 tracker,
725 )
726 }
727 pub(crate) fn contains(
728 schema_path: Location,
729 tracker: impl Into<LazyEvaluationPath>,
730 instance_path: Location,
731 instance: &'a Value,
732 ) -> ValidationError<'a> {
733 Self::borrowed(
734 instance,
735 ValidationErrorKind::Contains,
736 instance_path,
737 schema_path,
738 tracker,
739 )
740 }
741 pub(crate) fn content_encoding(
742 schema_path: Location,
743 tracker: impl Into<LazyEvaluationPath>,
744 instance_path: Location,
745 instance: &'a Value,
746 encoding: &str,
747 ) -> ValidationError<'a> {
748 Self::borrowed(
749 instance,
750 ValidationErrorKind::ContentEncoding {
751 content_encoding: encoding.to_string(),
752 },
753 instance_path,
754 schema_path,
755 tracker,
756 )
757 }
758 pub(crate) fn content_media_type(
759 schema_path: Location,
760 tracker: impl Into<LazyEvaluationPath>,
761 instance_path: Location,
762 instance: &'a Value,
763 media_type: &str,
764 ) -> ValidationError<'a> {
765 Self::borrowed(
766 instance,
767 ValidationErrorKind::ContentMediaType {
768 content_media_type: media_type.to_string(),
769 },
770 instance_path,
771 schema_path,
772 tracker,
773 )
774 }
775 pub(crate) fn enumeration(
776 schema_path: Location,
777 tracker: impl Into<LazyEvaluationPath>,
778 instance_path: Location,
779 instance: &'a Value,
780 options: &Value,
781 ) -> ValidationError<'a> {
782 Self::borrowed(
783 instance,
784 ValidationErrorKind::Enum {
785 options: options.clone(),
786 },
787 instance_path,
788 schema_path,
789 tracker,
790 )
791 }
792 pub(crate) fn exclusive_maximum(
793 schema_path: Location,
794 tracker: impl Into<LazyEvaluationPath>,
795 instance_path: Location,
796 instance: &'a Value,
797 limit: Value,
798 ) -> ValidationError<'a> {
799 Self::borrowed(
800 instance,
801 ValidationErrorKind::ExclusiveMaximum { limit },
802 instance_path,
803 schema_path,
804 tracker,
805 )
806 }
807 pub(crate) fn exclusive_minimum(
808 schema_path: Location,
809 tracker: impl Into<LazyEvaluationPath>,
810 instance_path: Location,
811 instance: &'a Value,
812 limit: Value,
813 ) -> ValidationError<'a> {
814 Self::borrowed(
815 instance,
816 ValidationErrorKind::ExclusiveMinimum { limit },
817 instance_path,
818 schema_path,
819 tracker,
820 )
821 }
822 pub(crate) fn false_schema(
823 schema_path: Location,
824 tracker: impl Into<LazyEvaluationPath>,
825 instance_path: Location,
826 instance: &'a Value,
827 ) -> ValidationError<'a> {
828 Self::borrowed(
829 instance,
830 ValidationErrorKind::FalseSchema,
831 instance_path,
832 schema_path,
833 tracker,
834 )
835 }
836 pub(crate) fn format(
837 schema_path: Location,
838 tracker: impl Into<LazyEvaluationPath>,
839 instance_path: Location,
840 instance: &'a Value,
841 format: impl Into<String>,
842 ) -> ValidationError<'a> {
843 Self::borrowed(
844 instance,
845 ValidationErrorKind::Format {
846 format: format.into(),
847 },
848 instance_path,
849 schema_path,
850 tracker,
851 )
852 }
853 pub(crate) fn from_utf8(error: FromUtf8Error) -> ValidationError<'a> {
854 ValidationError::new(
855 Cow::Owned(Value::Null),
856 ValidationErrorKind::FromUtf8 { error },
857 Location::new(),
858 Location::new(),
859 Location::new(),
860 )
861 }
862 pub(crate) fn max_items(
863 schema_path: Location,
864 tracker: impl Into<LazyEvaluationPath>,
865 instance_path: Location,
866 instance: &'a Value,
867 limit: u64,
868 ) -> ValidationError<'a> {
869 Self::borrowed(
870 instance,
871 ValidationErrorKind::MaxItems { limit },
872 instance_path,
873 schema_path,
874 tracker,
875 )
876 }
877 pub(crate) fn maximum(
878 schema_path: Location,
879 tracker: impl Into<LazyEvaluationPath>,
880 instance_path: Location,
881 instance: &'a Value,
882 limit: Value,
883 ) -> ValidationError<'a> {
884 Self::borrowed(
885 instance,
886 ValidationErrorKind::Maximum { limit },
887 instance_path,
888 schema_path,
889 tracker,
890 )
891 }
892 pub(crate) fn max_length(
893 schema_path: Location,
894 tracker: impl Into<LazyEvaluationPath>,
895 instance_path: Location,
896 instance: &'a Value,
897 limit: u64,
898 ) -> ValidationError<'a> {
899 Self::borrowed(
900 instance,
901 ValidationErrorKind::MaxLength { limit },
902 instance_path,
903 schema_path,
904 tracker,
905 )
906 }
907 pub(crate) fn max_properties(
908 schema_path: Location,
909 tracker: impl Into<LazyEvaluationPath>,
910 instance_path: Location,
911 instance: &'a Value,
912 limit: u64,
913 ) -> ValidationError<'a> {
914 Self::borrowed(
915 instance,
916 ValidationErrorKind::MaxProperties { limit },
917 instance_path,
918 schema_path,
919 tracker,
920 )
921 }
922 pub(crate) fn min_items(
923 schema_path: Location,
924 tracker: impl Into<LazyEvaluationPath>,
925 instance_path: Location,
926 instance: &'a Value,
927 limit: u64,
928 ) -> ValidationError<'a> {
929 Self::borrowed(
930 instance,
931 ValidationErrorKind::MinItems { limit },
932 instance_path,
933 schema_path,
934 tracker,
935 )
936 }
937 pub(crate) fn minimum(
938 schema_path: Location,
939 tracker: impl Into<LazyEvaluationPath>,
940 instance_path: Location,
941 instance: &'a Value,
942 limit: Value,
943 ) -> ValidationError<'a> {
944 Self::borrowed(
945 instance,
946 ValidationErrorKind::Minimum { limit },
947 instance_path,
948 schema_path,
949 tracker,
950 )
951 }
952 pub(crate) fn min_length(
953 schema_path: Location,
954 tracker: impl Into<LazyEvaluationPath>,
955 instance_path: Location,
956 instance: &'a Value,
957 limit: u64,
958 ) -> ValidationError<'a> {
959 Self::borrowed(
960 instance,
961 ValidationErrorKind::MinLength { limit },
962 instance_path,
963 schema_path,
964 tracker,
965 )
966 }
967 pub(crate) fn min_properties(
968 schema_path: Location,
969 tracker: impl Into<LazyEvaluationPath>,
970 instance_path: Location,
971 instance: &'a Value,
972 limit: u64,
973 ) -> ValidationError<'a> {
974 Self::borrowed(
975 instance,
976 ValidationErrorKind::MinProperties { limit },
977 instance_path,
978 schema_path,
979 tracker,
980 )
981 }
982 #[cfg(feature = "arbitrary-precision")]
983 pub(crate) fn multiple_of(
984 schema_path: Location,
985 tracker: impl Into<LazyEvaluationPath>,
986 instance_path: Location,
987 instance: &'a Value,
988 multiple_of: Value,
989 ) -> ValidationError<'a> {
990 Self::borrowed(
991 instance,
992 ValidationErrorKind::MultipleOf { multiple_of },
993 instance_path,
994 schema_path,
995 tracker,
996 )
997 }
998
999 #[cfg(not(feature = "arbitrary-precision"))]
1000 pub(crate) fn multiple_of(
1001 schema_path: Location,
1002 tracker: impl Into<LazyEvaluationPath>,
1003 instance_path: Location,
1004 instance: &'a Value,
1005 multiple_of: f64,
1006 ) -> ValidationError<'a> {
1007 Self::borrowed(
1008 instance,
1009 ValidationErrorKind::MultipleOf { multiple_of },
1010 instance_path,
1011 schema_path,
1012 tracker,
1013 )
1014 }
1015 pub(crate) fn not(
1016 schema_path: Location,
1017 tracker: impl Into<LazyEvaluationPath>,
1018 instance_path: Location,
1019 instance: &'a Value,
1020 schema: Value,
1021 ) -> ValidationError<'a> {
1022 Self::borrowed(
1023 instance,
1024 ValidationErrorKind::Not { schema },
1025 instance_path,
1026 schema_path,
1027 tracker,
1028 )
1029 }
1030 pub(crate) fn one_of_multiple_valid(
1031 schema_path: Location,
1032 tracker: impl Into<LazyEvaluationPath>,
1033 instance_path: Location,
1034 instance: &'a Value,
1035 context: Vec<Vec<ValidationError<'a>>>,
1036 ) -> ValidationError<'a> {
1037 let context = context
1038 .into_iter()
1039 .map(|errors| errors.into_iter().map(ValidationError::to_owned).collect())
1040 .collect::<Vec<_>>();
1041
1042 Self::borrowed(
1043 instance,
1044 ValidationErrorKind::OneOfMultipleValid { context },
1045 instance_path,
1046 schema_path,
1047 tracker,
1048 )
1049 }
1050 pub(crate) fn one_of_not_valid(
1051 schema_path: Location,
1052 tracker: impl Into<LazyEvaluationPath>,
1053 instance_path: Location,
1054 instance: &'a Value,
1055 context: Vec<Vec<ValidationError<'a>>>,
1056 ) -> ValidationError<'a> {
1057 let context = context
1058 .into_iter()
1059 .map(|errors| errors.into_iter().map(ValidationError::to_owned).collect())
1060 .collect::<Vec<_>>();
1061
1062 Self::borrowed(
1063 instance,
1064 ValidationErrorKind::OneOfNotValid { context },
1065 instance_path,
1066 schema_path,
1067 tracker,
1068 )
1069 }
1070 pub(crate) fn pattern(
1071 schema_path: Location,
1072 tracker: impl Into<LazyEvaluationPath>,
1073 instance_path: Location,
1074 instance: &'a Value,
1075 pattern: String,
1076 ) -> ValidationError<'a> {
1077 Self::borrowed(
1078 instance,
1079 ValidationErrorKind::Pattern { pattern },
1080 instance_path,
1081 schema_path,
1082 tracker,
1083 )
1084 }
1085 pub(crate) fn property_names(
1086 schema_path: Location,
1087 tracker: impl Into<LazyEvaluationPath>,
1088 instance_path: Location,
1089 instance: &'a Value,
1090 error: ValidationError<'a>,
1091 ) -> ValidationError<'a> {
1092 Self::borrowed(
1093 instance,
1094 ValidationErrorKind::PropertyNames {
1095 error: Box::new(error.to_owned()),
1096 },
1097 instance_path,
1098 schema_path,
1099 tracker,
1100 )
1101 }
1102 pub(crate) fn required(
1103 schema_path: Location,
1104 tracker: impl Into<LazyEvaluationPath>,
1105 instance_path: Location,
1106 instance: &'a Value,
1107 property: Value,
1108 ) -> ValidationError<'a> {
1109 Self::borrowed(
1110 instance,
1111 ValidationErrorKind::Required { property },
1112 instance_path,
1113 schema_path,
1114 tracker,
1115 )
1116 }
1117
1118 pub(crate) fn single_type_error(
1119 schema_path: Location,
1120 tracker: impl Into<LazyEvaluationPath>,
1121 instance_path: Location,
1122 instance: &'a Value,
1123 type_name: JsonType,
1124 ) -> ValidationError<'a> {
1125 Self::borrowed(
1126 instance,
1127 ValidationErrorKind::Type {
1128 kind: TypeKind::Single(type_name),
1129 },
1130 instance_path,
1131 schema_path,
1132 tracker,
1133 )
1134 }
1135 pub(crate) fn multiple_type_error(
1136 schema_path: Location,
1137 tracker: impl Into<LazyEvaluationPath>,
1138 instance_path: Location,
1139 instance: &'a Value,
1140 types: JsonTypeSet,
1141 ) -> ValidationError<'a> {
1142 Self::borrowed(
1143 instance,
1144 ValidationErrorKind::Type {
1145 kind: TypeKind::Multiple(types),
1146 },
1147 instance_path,
1148 schema_path,
1149 tracker,
1150 )
1151 }
1152 pub(crate) fn unevaluated_items(
1153 schema_path: Location,
1154 tracker: impl Into<LazyEvaluationPath>,
1155 instance_path: Location,
1156 instance: &'a Value,
1157 unexpected: Vec<String>,
1158 ) -> ValidationError<'a> {
1159 Self::borrowed(
1160 instance,
1161 ValidationErrorKind::UnevaluatedItems { unexpected },
1162 instance_path,
1163 schema_path,
1164 tracker,
1165 )
1166 }
1167 pub(crate) fn unevaluated_properties(
1168 schema_path: Location,
1169 tracker: impl Into<LazyEvaluationPath>,
1170 instance_path: Location,
1171 instance: &'a Value,
1172 unexpected: Vec<String>,
1173 ) -> ValidationError<'a> {
1174 Self::borrowed(
1175 instance,
1176 ValidationErrorKind::UnevaluatedProperties { unexpected },
1177 instance_path,
1178 schema_path,
1179 tracker,
1180 )
1181 }
1182 pub(crate) fn unique_items(
1183 schema_path: Location,
1184 tracker: impl Into<LazyEvaluationPath>,
1185 instance_path: Location,
1186 instance: &'a Value,
1187 ) -> ValidationError<'a> {
1188 Self::borrowed(
1189 instance,
1190 ValidationErrorKind::UniqueItems,
1191 instance_path,
1192 schema_path,
1193 tracker,
1194 )
1195 }
1196 pub fn custom(message: impl Into<String>) -> ValidationError<'static> {
1214 ValidationError::new(
1215 Cow::Owned(Value::Null),
1216 ValidationErrorKind::Custom {
1217 keyword: String::new(),
1218 message: message.into(),
1219 },
1220 Location::new(),
1221 Location::new(),
1222 Location::new(),
1223 )
1224 }
1225
1226 pub fn schema(message: impl Into<String>) -> ValidationError<'static> {
1231 ValidationError::new(
1232 Cow::Owned(Value::Null),
1233 ValidationErrorKind::Custom {
1234 keyword: String::new(),
1235 message: message.into(),
1236 },
1237 Location::new(),
1238 Location::new(),
1239 Location::new(),
1240 )
1241 }
1242
1243 pub(crate) fn with_context<'i>(
1248 self,
1249 instance: &'i Value,
1250 instance_path: &LazyLocation,
1251 schema_path: &Location,
1252 keyword: &str,
1253 ) -> ValidationError<'i> {
1254 let kind = match self.repr.kind {
1255 ValidationErrorKind::Custom { message, .. } => ValidationErrorKind::Custom {
1256 keyword: keyword.to_string(),
1257 message,
1258 },
1259 other => other,
1260 };
1261 ValidationError::new(
1262 Cow::Borrowed(instance),
1263 kind,
1264 instance_path.into(),
1265 schema_path.clone(),
1266 LazyEvaluationPath::SameAsSchemaPath,
1268 )
1269 }
1270
1271 pub(crate) fn with_schema_context<'s>(
1273 self,
1274 schema_value: &'s Value,
1275 schema_path: Location,
1276 keyword: &str,
1277 ) -> ValidationError<'s> {
1278 let kind = match self.repr.kind {
1279 ValidationErrorKind::Custom { message, .. } => ValidationErrorKind::Custom {
1280 keyword: keyword.into(),
1281 message,
1282 },
1283 other => other,
1284 };
1285 ValidationError::new(
1286 Cow::Borrowed(schema_value),
1287 kind,
1288 Location::new(),
1289 schema_path.clone(),
1290 schema_path,
1291 )
1292 }
1293
1294 pub(crate) fn compile_error(
1295 schema_path: Location,
1296 tracker: impl Into<LazyEvaluationPath>,
1297 instance_path: Location,
1298 instance: &'a Value,
1299 message: impl Into<String>,
1300 ) -> ValidationError<'a> {
1301 Self::borrowed(
1302 instance,
1303 ValidationErrorKind::Custom {
1304 keyword: String::new(),
1305 message: message.into(),
1306 },
1307 instance_path,
1308 schema_path,
1309 tracker,
1310 )
1311 }
1312}
1313
1314impl error::Error for ValidationError<'_> {}
1315impl From<referencing::Error> for ValidationError<'_> {
1316 #[inline]
1317 fn from(err: referencing::Error) -> Self {
1318 ValidationError::new(
1319 Cow::Owned(Value::Null),
1320 ValidationErrorKind::Referencing(err),
1321 Location::new(),
1322 Location::new(),
1323 Location::new(),
1324 )
1325 }
1326}
1327impl From<FromUtf8Error> for ValidationError<'_> {
1328 #[inline]
1329 fn from(err: FromUtf8Error) -> Self {
1330 ValidationError::from_utf8(err)
1331 }
1332}
1333
1334fn write_quoted_list(f: &mut Formatter<'_>, items: &[impl fmt::Display]) -> fmt::Result {
1335 let mut iter = items.iter();
1336 if let Some(item) = iter.next() {
1337 f.write_char('\'')?;
1338 write!(f, "{item}")?;
1339 f.write_char('\'')?;
1340 }
1341 for item in iter {
1342 f.write_str(", ")?;
1343 f.write_char('\'')?;
1344 write!(f, "{item}")?;
1345 f.write_char('\'')?;
1346 }
1347 Ok(())
1348}
1349
1350fn write_unexpected_suffix(f: &mut Formatter<'_>, len: usize) -> fmt::Result {
1351 f.write_str(if len == 1 {
1352 " was unexpected)"
1353 } else {
1354 " were unexpected)"
1355 })
1356}
1357
1358const MAX_DISPLAYED_ENUM_VARIANTS: usize = 3;
1359
1360fn write_enum_message(
1361 f: &mut Formatter<'_>,
1362 value: impl fmt::Display,
1363 options: &Value,
1364) -> fmt::Result {
1365 let array = options
1366 .as_array()
1367 .expect("Enum options must be a JSON array");
1368
1369 write!(f, "{value} is not one of ")?;
1370
1371 let total_count = array.len();
1372
1373 if total_count <= MAX_DISPLAYED_ENUM_VARIANTS {
1374 for (i, option) in array.iter().enumerate() {
1376 if i == 0 {
1377 write!(f, "{option}")?;
1378 } else if i == total_count - 1 {
1379 write!(f, " or {option}")?;
1380 } else {
1381 write!(f, ", {option}")?;
1382 }
1383 }
1384 } else {
1385 let show_count = MAX_DISPLAYED_ENUM_VARIANTS - 1;
1387 for (i, option) in array.iter().take(show_count).enumerate() {
1388 if i == 0 {
1389 write!(f, "{option}")?;
1390 } else {
1391 write!(f, ", {option}")?;
1392 }
1393 }
1394 let remaining = total_count - show_count;
1395 write!(f, " or {remaining} other candidates")?;
1396 }
1397 Ok(())
1398}
1399
1400impl fmt::Display for ValidationError<'_> {
1402 #[allow(clippy::too_many_lines)] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1404 match self.kind() {
1405 ValidationErrorKind::Referencing(error) => error.fmt(f),
1406 ValidationErrorKind::BacktrackLimitExceeded { error } => error.fmt(f),
1407 ValidationErrorKind::Format { format } => {
1408 write!(f, r#"{} is not a "{}""#, self.instance(), format)
1409 }
1410 ValidationErrorKind::AdditionalItems { limit } => {
1411 f.write_str("Additional items are not allowed (")?;
1412 let array = self.instance().as_array().expect("Always valid");
1413 let mut iter = array.iter().skip(*limit);
1414
1415 if let Some(item) = iter.next() {
1416 write!(f, "{item}")?;
1417 }
1418 for item in iter {
1419 f.write_str(", ")?;
1420 write!(f, "{item}")?;
1421 }
1422
1423 write_unexpected_suffix(f, array.len() - limit)
1424 }
1425 ValidationErrorKind::AdditionalProperties { unexpected } => {
1426 f.write_str("Additional properties are not allowed (")?;
1427 write_quoted_list(f, unexpected)?;
1428 write_unexpected_suffix(f, unexpected.len())
1429 }
1430 ValidationErrorKind::AnyOf { context: _ } => write!(
1431 f,
1432 "{} is not valid under any of the schemas listed in the 'anyOf' keyword",
1433 self.instance()
1434 ),
1435 ValidationErrorKind::OneOfNotValid { context: _ } => write!(
1436 f,
1437 "{} is not valid under any of the schemas listed in the 'oneOf' keyword",
1438 self.instance()
1439 ),
1440 ValidationErrorKind::Contains => write!(
1441 f,
1442 "None of {} are valid under the given schema",
1443 self.instance()
1444 ),
1445 ValidationErrorKind::Constant { expected_value } => {
1446 write!(f, "{expected_value} was expected")
1447 }
1448 ValidationErrorKind::ContentEncoding { content_encoding } => {
1449 write!(
1450 f,
1451 r#"{} is not compliant with "{}" content encoding"#,
1452 self.instance(),
1453 content_encoding
1454 )
1455 }
1456 ValidationErrorKind::ContentMediaType { content_media_type } => {
1457 write!(
1458 f,
1459 r#"{} is not compliant with "{}" media type"#,
1460 self.instance(),
1461 content_media_type
1462 )
1463 }
1464 ValidationErrorKind::FromUtf8 { error } => error.fmt(f),
1465 ValidationErrorKind::Enum { options } => {
1466 write_enum_message(f, self.instance(), options)
1467 }
1468 ValidationErrorKind::ExclusiveMaximum { limit } => write!(
1469 f,
1470 "{} is greater than or equal to the maximum of {}",
1471 self.instance(),
1472 limit
1473 ),
1474 ValidationErrorKind::ExclusiveMinimum { limit } => write!(
1475 f,
1476 "{} is less than or equal to the minimum of {}",
1477 self.instance(),
1478 limit
1479 ),
1480 ValidationErrorKind::FalseSchema => {
1481 write!(f, "False schema does not allow {}", self.instance())
1482 }
1483 ValidationErrorKind::Maximum { limit } => write!(
1484 f,
1485 "{} is greater than the maximum of {}",
1486 self.instance(),
1487 limit
1488 ),
1489 ValidationErrorKind::Minimum { limit } => {
1490 write!(
1491 f,
1492 "{} is less than the minimum of {}",
1493 self.instance(),
1494 limit
1495 )
1496 }
1497 ValidationErrorKind::MaxLength { limit } => write!(
1498 f,
1499 "{} is longer than {} character{}",
1500 self.instance(),
1501 limit,
1502 if *limit == 1 { "" } else { "s" }
1503 ),
1504 ValidationErrorKind::MinLength { limit } => write!(
1505 f,
1506 "{} is shorter than {} character{}",
1507 self.instance(),
1508 limit,
1509 if *limit == 1 { "" } else { "s" }
1510 ),
1511 ValidationErrorKind::MaxItems { limit } => write!(
1512 f,
1513 "{} has more than {} item{}",
1514 self.instance(),
1515 limit,
1516 if *limit == 1 { "" } else { "s" }
1517 ),
1518 ValidationErrorKind::MinItems { limit } => write!(
1519 f,
1520 "{} has less than {} item{}",
1521 self.instance(),
1522 limit,
1523 if *limit == 1 { "" } else { "s" }
1524 ),
1525 ValidationErrorKind::MaxProperties { limit } => write!(
1526 f,
1527 "{} has more than {} propert{}",
1528 self.instance(),
1529 limit,
1530 if *limit == 1 { "y" } else { "ies" }
1531 ),
1532 ValidationErrorKind::MinProperties { limit } => write!(
1533 f,
1534 "{} has less than {} propert{}",
1535 self.instance(),
1536 limit,
1537 if *limit == 1 { "y" } else { "ies" }
1538 ),
1539 ValidationErrorKind::Not { schema } => {
1540 write!(f, "{} is not allowed for {}", schema, self.instance())
1541 }
1542 ValidationErrorKind::OneOfMultipleValid { .. } => write!(
1543 f,
1544 "{} is valid under more than one of the schemas listed in the 'oneOf' keyword",
1545 self.instance()
1546 ),
1547 ValidationErrorKind::Pattern { pattern } => {
1548 write!(f, r#"{} does not match "{}""#, self.instance(), pattern)
1549 }
1550 ValidationErrorKind::PropertyNames { error } => error.fmt(f),
1551 ValidationErrorKind::Required { property } => {
1552 write!(f, "{property} is a required property")
1553 }
1554 ValidationErrorKind::MultipleOf { multiple_of } => {
1555 write!(
1556 f,
1557 "{} is not a multiple of {}",
1558 self.instance(),
1559 multiple_of
1560 )
1561 }
1562 ValidationErrorKind::UnevaluatedItems { unexpected } => {
1563 f.write_str("Unevaluated items are not allowed (")?;
1564 write_quoted_list(f, unexpected)?;
1565 write_unexpected_suffix(f, unexpected.len())
1566 }
1567 ValidationErrorKind::UnevaluatedProperties { unexpected } => {
1568 f.write_str("Unevaluated properties are not allowed (")?;
1569 write_quoted_list(f, unexpected)?;
1570 write_unexpected_suffix(f, unexpected.len())
1571 }
1572 ValidationErrorKind::UniqueItems => {
1573 write!(f, "{} has non-unique elements", self.instance())
1574 }
1575 ValidationErrorKind::Type {
1576 kind: TypeKind::Single(type_),
1577 } => write!(f, r#"{} is not of type "{}""#, self.instance(), type_),
1578 ValidationErrorKind::Type {
1579 kind: TypeKind::Multiple(types),
1580 } => {
1581 write!(f, "{} is not of types ", self.instance())?;
1582 let mut iter = types.iter();
1583 if let Some(t) = iter.next() {
1584 f.write_char('"')?;
1585 write!(f, "{t}")?;
1586 f.write_char('"')?;
1587 }
1588 for t in iter {
1589 f.write_str(", ")?;
1590 f.write_char('"')?;
1591 write!(f, "{t}")?;
1592 f.write_char('"')?;
1593 }
1594 Ok(())
1595 }
1596 ValidationErrorKind::Custom { message, .. } => f.write_str(message),
1597 }
1598 }
1599}
1600
1601pub struct MaskedValidationError<'a, 'b, 'c> {
1603 error: &'b ValidationError<'a>,
1604 placeholder: Cow<'c, str>,
1605}
1606
1607impl fmt::Display for MaskedValidationError<'_, '_, '_> {
1608 #[allow(clippy::too_many_lines)]
1609 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1610 match self.error.kind() {
1611 ValidationErrorKind::Referencing(error) => error.fmt(f),
1612 ValidationErrorKind::BacktrackLimitExceeded { error } => error.fmt(f),
1613 ValidationErrorKind::Format { format } => {
1614 write!(f, r#"{} is not a "{format}""#, self.placeholder)
1615 }
1616 ValidationErrorKind::AdditionalItems { limit } => {
1617 write!(f, "Additional items are not allowed ({limit} items)")
1618 }
1619 ValidationErrorKind::AdditionalProperties { unexpected } => {
1620 f.write_str("Additional properties are not allowed (")?;
1621 write_quoted_list(f, unexpected)?;
1622 write_unexpected_suffix(f, unexpected.len())
1623 }
1624 ValidationErrorKind::AnyOf { .. } => write!(
1625 f,
1626 "{} is not valid under any of the schemas listed in the 'anyOf' keyword",
1627 self.placeholder
1628 ),
1629 ValidationErrorKind::OneOfNotValid { context: _ } => write!(
1630 f,
1631 "{} is not valid under any of the schemas listed in the 'oneOf' keyword",
1632 self.placeholder
1633 ),
1634 ValidationErrorKind::Contains => write!(
1635 f,
1636 "None of {} are valid under the given schema",
1637 self.placeholder
1638 ),
1639 ValidationErrorKind::Constant { expected_value } => {
1640 write!(f, "{expected_value} was expected")
1641 }
1642 ValidationErrorKind::ContentEncoding { content_encoding } => {
1643 write!(
1644 f,
1645 r#"{} is not compliant with "{}" content encoding"#,
1646 self.placeholder, content_encoding
1647 )
1648 }
1649 ValidationErrorKind::ContentMediaType { content_media_type } => {
1650 write!(
1651 f,
1652 r#"{} is not compliant with "{}" media type"#,
1653 self.placeholder, content_media_type
1654 )
1655 }
1656 ValidationErrorKind::FromUtf8 { error } => error.fmt(f),
1657 ValidationErrorKind::Enum { options } => {
1658 write_enum_message(f, &self.placeholder, options)
1659 }
1660 ValidationErrorKind::ExclusiveMaximum { limit } => write!(
1661 f,
1662 "{} is greater than or equal to the maximum of {}",
1663 self.placeholder, limit
1664 ),
1665 ValidationErrorKind::ExclusiveMinimum { limit } => write!(
1666 f,
1667 "{} is less than or equal to the minimum of {}",
1668 self.placeholder, limit
1669 ),
1670 ValidationErrorKind::FalseSchema => {
1671 write!(f, "False schema does not allow {}", self.placeholder)
1672 }
1673 ValidationErrorKind::Maximum { limit } => write!(
1674 f,
1675 "{} is greater than the maximum of {}",
1676 self.placeholder, limit
1677 ),
1678 ValidationErrorKind::Minimum { limit } => {
1679 write!(
1680 f,
1681 "{} is less than the minimum of {}",
1682 self.placeholder, limit
1683 )
1684 }
1685 ValidationErrorKind::MaxLength { limit } => write!(
1686 f,
1687 "{} is longer than {} character{}",
1688 self.placeholder,
1689 limit,
1690 if *limit == 1 { "" } else { "s" }
1691 ),
1692 ValidationErrorKind::MinLength { limit } => write!(
1693 f,
1694 "{} is shorter than {} character{}",
1695 self.placeholder,
1696 limit,
1697 if *limit == 1 { "" } else { "s" }
1698 ),
1699 ValidationErrorKind::MaxItems { limit } => write!(
1700 f,
1701 "{} has more than {} item{}",
1702 self.placeholder,
1703 limit,
1704 if *limit == 1 { "" } else { "s" }
1705 ),
1706 ValidationErrorKind::MinItems { limit } => write!(
1707 f,
1708 "{} has less than {} item{}",
1709 self.placeholder,
1710 limit,
1711 if *limit == 1 { "" } else { "s" }
1712 ),
1713 ValidationErrorKind::MaxProperties { limit } => write!(
1714 f,
1715 "{} has more than {} propert{}",
1716 self.placeholder,
1717 limit,
1718 if *limit == 1 { "y" } else { "ies" }
1719 ),
1720 ValidationErrorKind::MinProperties { limit } => write!(
1721 f,
1722 "{} has less than {} propert{}",
1723 self.placeholder,
1724 limit,
1725 if *limit == 1 { "y" } else { "ies" }
1726 ),
1727 ValidationErrorKind::Not { schema } => {
1728 write!(f, "{} is not allowed for {}", schema, self.placeholder)
1729 }
1730 ValidationErrorKind::OneOfMultipleValid { .. } => write!(
1731 f,
1732 "{} is valid under more than one of the schemas listed in the 'oneOf' keyword",
1733 self.placeholder
1734 ),
1735 ValidationErrorKind::Pattern { pattern } => {
1736 write!(f, r#"{} does not match "{}""#, self.placeholder, pattern)
1737 }
1738 ValidationErrorKind::PropertyNames { error } => error.fmt(f),
1739 ValidationErrorKind::Required { property } => {
1740 write!(f, "{property} is a required property")
1741 }
1742 ValidationErrorKind::MultipleOf { multiple_of } => {
1743 write!(
1744 f,
1745 "{} is not a multiple of {}",
1746 self.placeholder, multiple_of
1747 )
1748 }
1749 ValidationErrorKind::UnevaluatedItems { unexpected } => {
1750 write!(
1751 f,
1752 "Unevaluated items are not allowed ({} items)",
1753 unexpected.len()
1754 )
1755 }
1756 ValidationErrorKind::UnevaluatedProperties { unexpected } => {
1757 f.write_str("Unevaluated properties are not allowed (")?;
1758 write_quoted_list(f, unexpected)?;
1759 write_unexpected_suffix(f, unexpected.len())
1760 }
1761 ValidationErrorKind::UniqueItems => {
1762 write!(f, "{} has non-unique elements", self.placeholder)
1763 }
1764 ValidationErrorKind::Type {
1765 kind: TypeKind::Single(type_),
1766 } => write!(f, r#"{} is not of type "{}""#, self.placeholder, type_),
1767 ValidationErrorKind::Type {
1768 kind: TypeKind::Multiple(types),
1769 } => {
1770 write!(f, "{} is not of types ", self.placeholder)?;
1771 let mut iter = types.iter();
1772 if let Some(t) = iter.next() {
1773 f.write_char('"')?;
1774 write!(f, "{t}")?;
1775 f.write_char('"')?;
1776 }
1777 for t in iter {
1778 f.write_str(", ")?;
1779 f.write_char('"')?;
1780 write!(f, "{t}")?;
1781 f.write_char('"')?;
1782 }
1783 Ok(())
1784 }
1785 ValidationErrorKind::Custom { message, .. } => f.write_str(message),
1786 }
1787 }
1788}
1789
1790#[cfg(test)]
1791mod tests {
1792 use super::*;
1793 use referencing::{Registry, Resource};
1794 use serde_json::json;
1795
1796 use test_case::test_case;
1797
1798 fn owned_error(instance: Value, kind: ValidationErrorKind) -> ValidationError<'static> {
1799 ValidationError::new(
1800 Cow::Owned(instance),
1801 kind,
1802 Location::new(),
1803 Location::new(),
1804 Location::new(),
1805 )
1806 }
1807
1808 #[test]
1809 fn error_iterator_into_errors_collects_all_errors() {
1810 let iterator = ErrorIterator::from_iterator(
1811 vec![
1812 owned_error(json!(1), ValidationErrorKind::Minimum { limit: json!(2) }),
1813 owned_error(json!(3), ValidationErrorKind::Maximum { limit: json!(2) }),
1814 ]
1815 .into_iter(),
1816 );
1817 let validation_errors = iterator.into_errors();
1818 let collected: Vec<_> = validation_errors.into_iter().collect();
1819 assert_eq!(collected.len(), 2);
1820 assert_eq!(collected[0].to_string(), "1 is less than the minimum of 2");
1821 assert_eq!(
1822 collected[1].to_string(),
1823 "3 is greater than the maximum of 2"
1824 );
1825 }
1826
1827 #[test]
1828 fn validation_errors_display_reports_success() {
1829 let errors = ValidationErrors { errors: Vec::new() };
1830 assert_eq!(format!("{errors}"), "Validation succeeded");
1831 }
1832
1833 #[test]
1834 fn validation_errors_display_lists_messages() {
1835 let errors = ValidationErrors {
1836 errors: vec![
1837 owned_error(json!(1), ValidationErrorKind::Minimum { limit: json!(2) }),
1838 owned_error(json!(3), ValidationErrorKind::Maximum { limit: json!(2) }),
1839 ],
1840 };
1841 let rendered = format!("{errors}");
1842 assert!(rendered.contains("Validation errors:"));
1843 assert!(rendered.contains("01: 1 is less than the minimum of 2"));
1844 assert!(rendered.contains("02: 3 is greater than the maximum of 2"));
1845 }
1846
1847 #[test]
1848 fn validation_errors_len_and_is_empty() {
1849 let empty = ValidationErrors { errors: vec![] };
1850 assert_eq!(empty.len(), 0);
1851 assert!(empty.is_empty());
1852
1853 let errors = ValidationErrors {
1854 errors: vec![owned_error(
1855 json!(1),
1856 ValidationErrorKind::Minimum { limit: json!(2) },
1857 )],
1858 };
1859 assert_eq!(errors.len(), 1);
1860 assert!(!errors.is_empty());
1861 }
1862
1863 #[test]
1864 fn validation_errors_as_slice() {
1865 let errors = ValidationErrors {
1866 errors: vec![
1867 owned_error(json!(1), ValidationErrorKind::Minimum { limit: json!(2) }),
1868 owned_error(json!(3), ValidationErrorKind::Maximum { limit: json!(2) }),
1869 ],
1870 };
1871
1872 let slice = errors.as_slice();
1873 assert_eq!(slice.len(), 2);
1874 assert_eq!(slice[0].to_string(), "1 is less than the minimum of 2");
1875 assert_eq!(slice[1].to_string(), "3 is greater than the maximum of 2");
1876 }
1877
1878 #[test]
1879 fn validation_errors_iter() {
1880 let errors = ValidationErrors {
1881 errors: vec![
1882 owned_error(json!(1), ValidationErrorKind::Minimum { limit: json!(2) }),
1883 owned_error(json!(3), ValidationErrorKind::Maximum { limit: json!(2) }),
1884 ],
1885 };
1886
1887 let collected: Vec<_> = errors.iter().map(ValidationError::to_string).collect();
1888 assert_eq!(collected.len(), 2);
1889 assert_eq!(collected[0], "1 is less than the minimum of 2");
1890 assert_eq!(collected[1], "3 is greater than the maximum of 2");
1891 }
1892
1893 #[test]
1894 #[allow(clippy::explicit_iter_loop)]
1895 fn validation_errors_iter_mut() {
1896 let mut errors = ValidationErrors {
1897 errors: vec![owned_error(
1898 json!(1),
1899 ValidationErrorKind::Minimum { limit: json!(2) },
1900 )],
1901 };
1902
1903 for error in errors.iter_mut() {
1905 let _ = error.to_string();
1906 }
1907 }
1908
1909 #[test]
1910 fn validation_errors_into_iterator_by_ref() {
1911 let errors = ValidationErrors {
1912 errors: vec![owned_error(
1913 json!(1),
1914 ValidationErrorKind::Minimum { limit: json!(2) },
1915 )],
1916 };
1917
1918 let collected: Vec<_> = (&errors).into_iter().collect();
1919 assert_eq!(collected.len(), 1);
1920 assert_eq!(errors.len(), 1);
1922 }
1923
1924 #[test]
1925 fn validation_errors_into_iterator_by_mut_ref() {
1926 let mut errors = ValidationErrors {
1927 errors: vec![owned_error(
1928 json!(1),
1929 ValidationErrorKind::Minimum { limit: json!(2) },
1930 )],
1931 };
1932
1933 let collected: Vec<_> = (&mut errors).into_iter().collect();
1934 assert_eq!(collected.len(), 1);
1935 assert_eq!(errors.len(), 1);
1937 }
1938
1939 #[test]
1940 fn error_iterator_size_hint() {
1941 let vec = vec![
1942 owned_error(json!(1), ValidationErrorKind::Minimum { limit: json!(2) }),
1943 owned_error(json!(3), ValidationErrorKind::Maximum { limit: json!(2) }),
1944 ];
1945 let iterator = ErrorIterator::from_iterator(vec.into_iter());
1946 let (lower, upper) = iterator.size_hint();
1947 assert_eq!(lower, 2);
1948 assert_eq!(upper, Some(2));
1949 }
1950
1951 #[test]
1952 fn validation_errors_debug() {
1953 let errors = ValidationErrors {
1954 errors: vec![owned_error(
1955 json!(1),
1956 ValidationErrorKind::Minimum { limit: json!(2) },
1957 )],
1958 };
1959 let debug_output = format!("{errors:?}");
1960 assert!(debug_output.contains("ValidationErrors"));
1961 assert!(debug_output.contains("errors"));
1962 }
1963
1964 #[test]
1965 fn single_type_error() {
1966 let instance = json!(42);
1967 let err = ValidationError::single_type_error(
1968 Location::new(),
1969 Location::new(),
1970 Location::new(),
1971 &instance,
1972 JsonType::String,
1973 );
1974 assert_eq!(err.to_string(), r#"42 is not of type "string""#);
1975 }
1976
1977 #[test]
1978 fn multiple_types_error() {
1979 let instance = json!(42);
1980 let types = JsonTypeSet::empty()
1981 .insert(JsonType::String)
1982 .insert(JsonType::Number);
1983 let err = ValidationError::multiple_type_error(
1984 Location::new(),
1985 Location::new(),
1986 Location::new(),
1987 &instance,
1988 types,
1989 );
1990 assert_eq!(err.to_string(), r#"42 is not of types "number", "string""#);
1991 }
1992
1993 #[test_case(true, &json!({"foo": {"bar": 42}}), "/foo/bar")]
1994 #[test_case(true, &json!({"foo": "a"}), "/foo")]
1995 #[test_case(false, &json!({"foo": {"bar": 42}}), "/foo/bar")]
1996 #[test_case(false, &json!({"foo": "a"}), "/foo")]
1997 fn instance_path_properties(additional_properties: bool, instance: &Value, expected: &str) {
1998 let schema = json!(
1999 {
2000 "additionalProperties": additional_properties,
2001 "type":"object",
2002 "properties":{
2003 "foo":{
2004 "type":"object",
2005 "properties":{
2006 "bar":{
2007 "type":"string"
2008 }
2009 }
2010 }
2011 }
2012 }
2013 );
2014 let validator = crate::validator_for(&schema).unwrap();
2015 let mut result = validator.iter_errors(instance);
2016 let error = result.next().expect("validation error");
2017
2018 assert!(result.next().is_none());
2019 assert_eq!(error.instance_path().as_str(), expected);
2020 }
2021
2022 #[test_case(true, &json!([1, {"foo": ["42"]}]), "/0")]
2023 #[test_case(true, &json!(["a", {"foo": [42]}]), "/1/foo/0")]
2024 #[test_case(false, &json!([1, {"foo": ["42"]}]), "/0")]
2025 #[test_case(false, &json!(["a", {"foo": [42]}]), "/1/foo/0")]
2026 fn instance_path_properties_and_arrays(
2027 additional_items: bool,
2028 instance: &Value,
2029 expected: &str,
2030 ) {
2031 let schema = json!(
2032 {
2033 "items": additional_items,
2034 "type": "array",
2035 "prefixItems": [
2036 {
2037 "type": "string"
2038 },
2039 {
2040 "type": "object",
2041 "properties": {
2042 "foo": {
2043 "type": "array",
2044 "prefixItems": [
2045 {
2046 "type": "string"
2047 }
2048 ]
2049 }
2050 }
2051 }
2052 ]
2053 }
2054 );
2055 let validator = crate::validator_for(&schema).unwrap();
2056 let mut result = validator.iter_errors(instance);
2057 let error = result.next().expect("validation error");
2058
2059 assert!(result.next().is_none());
2060 assert_eq!(error.instance_path().as_str(), expected);
2061 }
2062
2063 #[test_case(true, &json!([[1, 2, 3], [4, "5", 6], [7, 8, 9]]), "/1/1")]
2064 #[test_case(false, &json!([[1, 2, 3], [4, "5", 6], [7, 8, 9]]), "/1/1")]
2065 #[test_case(true, &json!([[1, 2, 3], [4, 5, 6], 42]), "/2")]
2066 #[test_case(false, &json!([[1, 2, 3], [4, 5, 6], 42]), "/2")]
2067 fn instance_path_nested_arrays(additional_items: bool, instance: &Value, expected: &str) {
2068 let schema = json!(
2069 {
2070 "additionalItems": additional_items,
2071 "type": "array",
2072 "items": {
2073 "type": "array",
2074 "items": {
2075 "type": "integer"
2076 }
2077 }
2078 }
2079 );
2080 let validator = crate::validator_for(&schema).unwrap();
2081 let mut result = validator.iter_errors(instance);
2082 let error = result.next().expect("validation error");
2083
2084 assert!(result.next().is_none());
2085 assert_eq!(error.instance_path().as_str(), expected);
2086 }
2087
2088 #[test_case(true, &json!([1, "a"]), "/1")]
2089 #[test_case(false, &json!([1, "a"]), "/1")]
2090 #[test_case(true, &json!(123), "")]
2091 #[test_case(false, &json!(123), "")]
2092 fn instance_path_arrays(additional_items: bool, instance: &Value, expected: &str) {
2093 let schema = json!(
2094 {
2095 "additionalItems": additional_items,
2096 "type": "array",
2097 "items": {
2098 "type": "integer"
2099 }
2100 }
2101 );
2102 let validator = crate::validator_for(&schema).unwrap();
2103 let mut result = validator.iter_errors(instance);
2104 let error = result.next().expect("validation error");
2105
2106 assert!(result.next().is_none());
2107 assert_eq!(error.instance_path().as_str(), expected);
2108 }
2109
2110 #[test_case(
2111 json!("2023-13-45"),
2112 ValidationErrorKind::Format {
2113 format: "date".to_string(),
2114 },
2115 "value is not a \"date\""
2116 )]
2117 #[test_case(
2118 json!("sensitive data"),
2119 ValidationErrorKind::MaxLength { limit: 5 },
2120 "value is longer than 5 characters"
2121 )]
2122 #[test_case(
2123 json!({"secret": "data", "key": "value"}),
2124 ValidationErrorKind::AdditionalProperties {
2125 unexpected: vec!["secret".to_string(), "key".to_string()]
2126 },
2127 "Additional properties are not allowed ('secret', 'key' were unexpected)"
2128 )]
2129 #[test_case(
2130 json!(123),
2131 ValidationErrorKind::Minimum { limit: json!(456) },
2132 "value is less than the minimum of 456"
2133 )]
2134 #[test_case(
2135 json!("secret_key_123"),
2136 ValidationErrorKind::Pattern {
2137 pattern: "^[A-Z0-9]{32}$".to_string(),
2138 },
2139 "value does not match \"^[A-Z0-9]{32}$\""
2140 )]
2141 #[test_case(
2142 json!([1, 2, 2, 3]),
2143 ValidationErrorKind::UniqueItems,
2144 "value has non-unique elements"
2145 )]
2146 #[test_case(
2147 json!(123),
2148 ValidationErrorKind::Type { kind: TypeKind::Single(JsonType::String) },
2149 "value is not of type \"string\""
2150 )]
2151 fn test_masked_error_messages(instance: Value, kind: ValidationErrorKind, expected: &str) {
2152 let error = ValidationError::new(
2153 Cow::Owned(instance),
2154 kind,
2155 Location::new(),
2156 Location::new(),
2157 Location::new(),
2158 );
2159 assert_eq!(error.masked().to_string(), expected);
2160 }
2161
2162 #[test_case(
2163 json!("sensitive data"),
2164 ValidationErrorKind::MaxLength { limit: 5 },
2165 "[REDACTED]",
2166 "[REDACTED] is longer than 5 characters"
2167 )]
2168 #[test_case(
2169 json!({"password": "secret123"}),
2170 ValidationErrorKind::Type {
2171 kind: TypeKind::Single(JsonType::String)
2172 },
2173 "***",
2174 "*** is not of type \"string\""
2175 )]
2176 fn test_custom_masked_error_messages(
2177 instance: Value,
2178 kind: ValidationErrorKind,
2179 placeholder: &str,
2180 expected: &str,
2181 ) {
2182 let error = ValidationError::new(
2183 Cow::Owned(instance),
2184 kind,
2185 Location::new(),
2186 Location::new(),
2187 Location::new(),
2188 );
2189 assert_eq!(error.masked_with(placeholder).to_string(), expected);
2190 }
2191
2192 #[test]
2193 fn test_absolute_keyword_location_absent_for_schema_without_base_uri() {
2194 let schema = serde_json::json!({"type": "string"});
2195 let instance = serde_json::json!(42);
2196 let err = crate::validate(&schema, &instance).unwrap_err();
2197 assert!(err.absolute_keyword_location().is_none());
2198 }
2199
2200 #[test]
2201 fn test_absolute_keyword_location_present_for_schema_with_base_uri() {
2202 let schema = serde_json::json!({
2203 "$id": "https://example.com/schema.json",
2204 "type": "string"
2205 });
2206 let instance = serde_json::json!(42);
2207 let validator = crate::options()
2208 .build(&schema)
2209 .expect("schema should compile");
2210 let err = validator.validate(&instance).unwrap_err();
2211 let absolute_location = err
2212 .absolute_keyword_location()
2213 .expect("should have absolute keyword location");
2214 assert_eq!(
2215 absolute_location.as_str(),
2216 "https://example.com/schema.json#/type"
2217 );
2218 }
2219
2220 #[test]
2221 fn test_absolute_keyword_location_from_iter_errors() {
2222 let schema = serde_json::json!({
2223 "$id": "https://example.com/schema.json",
2224 "type": "string"
2225 });
2226 let instance = serde_json::json!(42);
2227 let validator = crate::options()
2228 .build(&schema)
2229 .expect("schema should compile");
2230 let errs: Vec<_> = validator.iter_errors(&instance).collect();
2231 assert_eq!(errs.len(), 1);
2232 let absolute_location = errs[0]
2233 .absolute_keyword_location()
2234 .expect("iter_errors should set absolute keyword location");
2235 assert_eq!(
2236 absolute_location.as_str(),
2237 "https://example.com/schema.json#/type"
2238 );
2239 }
2240
2241 #[test]
2242 fn test_absolute_keyword_location_false_schema() {
2243 let schema = serde_json::json!(false);
2244 let instance = serde_json::json!(42);
2245 let validator = crate::options()
2246 .with_base_uri("https://example.com/schema.json")
2247 .build(&schema)
2248 .expect("schema should compile");
2249 let err = validator.validate(&instance).unwrap_err();
2250 let absolute_location = err
2251 .absolute_keyword_location()
2252 .expect("false schema should have absolute keyword location");
2253 assert_eq!(
2254 absolute_location.as_str(),
2255 "https://example.com/schema.json"
2256 );
2257 }
2258
2259 #[test]
2260 fn test_absolute_keyword_location_preserved_by_to_owned() {
2261 let schema = serde_json::json!({
2262 "$id": "https://example.com/schema.json",
2263 "type": "string"
2264 });
2265 let instance = serde_json::json!(42);
2266 let validator = crate::options()
2267 .build(&schema)
2268 .expect("schema should compile");
2269 let err = validator.validate(&instance).unwrap_err();
2270 let owned = err.to_owned();
2271 let absolute_location = owned
2272 .absolute_keyword_location()
2273 .expect("to_owned should preserve absolute keyword location");
2274 assert_eq!(
2275 absolute_location.as_str(),
2276 "https://example.com/schema.json#/type"
2277 );
2278 }
2279
2280 #[test]
2281 fn test_absolute_keyword_location_external_ref() {
2282 let external = serde_json::json!({
2283 "$id": "https://example.com/string.json",
2284 "type": "string"
2285 });
2286 let schema = serde_json::json!({
2287 "$id": "https://example.com/root.json",
2288 "$ref": "https://example.com/string.json"
2289 });
2290 let instance = serde_json::json!(42);
2291 let registry = Registry::new()
2292 .add(
2293 "https://example.com/string.json",
2294 Resource::from_contents(external),
2295 )
2296 .expect("external schema should be accepted")
2297 .prepare()
2298 .expect("registry should build");
2299 let validator = crate::options()
2300 .with_registry(®istry)
2301 .build(&schema)
2302 .expect("schema should compile");
2303 let err = validator.validate(&instance).unwrap_err();
2304 let absolute_location = err
2305 .absolute_keyword_location()
2306 .expect("external ref error should have absolute keyword location");
2307 assert_eq!(
2309 absolute_location.as_str(),
2310 "https://example.com/string.json#/type"
2311 );
2312 }
2313}