1use std::collections::{BTreeMap, BTreeSet};
38use std::fmt;
39
40use crate::CompiledSchema;
41use crate::datatypes::{DatatypeContext, DatatypeError, DatatypeRegistry};
42use crate::grammar::{Context, Param, Pattern};
43use crate::simplify::{choice, group, interleave, name_class_matches};
44
45type Definitions = BTreeMap<String, Pattern>;
46
47#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
52pub struct ExpandedName {
53 pub namespace: Option<String>,
55 pub local: String,
57}
58
59impl ExpandedName {
60 pub fn new(namespace: Option<impl Into<String>>, local: impl Into<String>) -> Self {
63 Self {
64 namespace: namespace.map(Into::into),
65 local: local.into(),
66 }
67 }
68}
69
70impl fmt::Display for ExpandedName {
71 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match &self.namespace {
73 Some(namespace) => write!(formatter, "{{{namespace}}}{}", self.local),
74 None => formatter.write_str(&self.local),
75 }
76 }
77}
78
79#[derive(Clone, Debug, Eq, PartialEq)]
83pub enum Content<E> {
84 Element(E),
86 Text(String),
91}
92
93pub trait Element: Sized {
97 type Location;
106 fn name(&self) -> ExpandedName;
108 fn attributes(&self) -> impl Iterator<Item = (ExpandedName, String)>;
113 fn children(&self) -> impl Iterator<Item = Content<Self>>;
115 fn location(&self) -> Option<Self::Location> {
118 None
119 }
120 fn namespace_bindings(&self) -> impl Iterator<Item = (String, String)> {
128 std::iter::empty()
129 }
130}
131
132pub fn validate<E: Element>(
139 schema: &CompiledSchema,
140 registry: &DatatypeRegistry,
141 root: &E,
142) -> Result<Vec<ValidationError<E::Location>>, DatatypeError> {
143 let mut checked_refs = BTreeSet::new();
144 check_datatypes_supported(
145 &schema.start,
146 &schema.definitions,
147 registry,
148 &mut checked_refs,
149 )?;
150 for pattern in schema.definitions.values() {
151 check_datatypes_supported(pattern, &schema.definitions, registry, &mut checked_refs)?;
152 }
153
154 let mut errors = Vec::new();
155 let mut path = Vec::new();
156 match find_element_contents(&schema.start, &root.name(), &schema.definitions) {
157 Some(content) => validate_element_content(
158 &content,
159 root,
160 &schema.definitions,
161 registry,
162 &mut path,
163 &mut errors,
164 ),
165 None => errors.push(ValidationError {
166 kind: ValidationErrorKind::UnexpectedElement(root.name()),
167 path: Vec::new(),
168 location: root.location(),
169 }),
170 }
171 Ok(errors)
172}
173
174#[derive(Clone, Debug, Eq, PartialEq)]
184pub struct ValidationError<L> {
185 kind: ValidationErrorKind,
186 path: Vec<ExpandedName>,
187 location: Option<L>,
188}
189
190impl<L> ValidationError<L> {
191 pub fn kind(&self) -> &ValidationErrorKind {
193 &self.kind
194 }
195 pub fn path(&self) -> &[ExpandedName] {
198 &self.path
199 }
200 pub fn location(&self) -> Option<&L> {
203 self.location.as_ref()
204 }
205}
206
207impl<L: fmt::Display> fmt::Display for ValidationError<L> {
208 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
209 write!(formatter, "{}", self.kind)?;
210 if let Some(location) = &self.location {
211 write!(formatter, " at {location}")?;
212 }
213 Ok(())
214 }
215}
216
217impl<L: fmt::Debug + fmt::Display> std::error::Error for ValidationError<L> {}
218
219#[derive(Clone, Debug, Eq, PartialEq)]
221pub enum ValidationErrorKind {
222 UnexpectedElement(ExpandedName),
224 UnexpectedText,
227 UnexpectedAttribute(ExpandedName),
229 InvalidAttributeValue(ExpandedName, String),
232 MissingContent,
235}
236
237impl fmt::Display for ValidationErrorKind {
238 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
239 match self {
240 Self::UnexpectedElement(name) => write!(formatter, "unexpected element `{name}`"),
241 Self::UnexpectedText => formatter.write_str("unexpected text"),
242 Self::UnexpectedAttribute(name) => write!(formatter, "unexpected attribute `{name}`"),
243 Self::InvalidAttributeValue(name, value) => {
244 write!(formatter, "invalid value `{value}` for attribute `{name}`")
245 }
246 Self::MissingContent => formatter.write_str("missing required content"),
247 }
248 }
249}
250
251fn validate_element_content<E: Element>(
252 content: &Pattern,
253 element: &E,
254 definitions: &Definitions,
255 registry: &DatatypeRegistry,
256 path: &mut Vec<ExpandedName>,
257 errors: &mut Vec<ValidationError<E::Location>>,
258) {
259 path.push(element.name());
260 let mut pattern = content.clone();
261 let context = DatatypeContext::from_bindings(element.namespace_bindings().collect());
262
263 for (name, value) in element.attributes() {
266 if !attribute_name_known(&pattern, &name, definitions) {
267 errors.push(ValidationError {
268 kind: ValidationErrorKind::UnexpectedAttribute(name),
269 path: path.clone(),
270 location: element.location(),
271 });
272 continue;
273 }
274 let derived = deriv_attribute(&pattern, &name, &value, definitions, registry, &context);
275 if matches!(derived, Pattern::NotAllowed) {
276 errors.push(ValidationError {
277 kind: ValidationErrorKind::InvalidAttributeValue(name, value),
278 path: path.clone(),
279 location: element.location(),
280 });
281 } else {
282 pattern = derived;
283 }
284 }
285
286 let children: Vec<_> = element.children().collect();
288 let strip_whitespace = children.len() > 1;
293 for child in children {
294 match child {
295 Content::Text(text) if strip_whitespace && text.trim().is_empty() => {}
296 Content::Text(text) => {
297 let derived = deriv_text(&pattern, &text, definitions, registry, &context);
298 if matches!(derived, Pattern::NotAllowed) {
299 if !text.trim().is_empty() || wants_text(&pattern, definitions) {
308 errors.push(ValidationError {
309 kind: ValidationErrorKind::UnexpectedText,
310 path: path.clone(),
311 location: element.location(),
312 });
313 }
314 } else {
315 pattern = derived;
316 }
317 }
318 Content::Element(child_element) => {
319 let name = child_element.name();
320 match find_element_contents(&pattern, &name, definitions) {
321 Some(child_content) => {
322 validate_element_content(
323 &child_content,
324 &child_element,
325 definitions,
326 registry,
327 path,
328 errors,
329 );
330 pattern = deriv_element_event(&pattern, &name, definitions);
331 }
332 None => errors.push(ValidationError {
333 kind: ValidationErrorKind::UnexpectedElement(name),
334 path: path.clone(),
335 location: child_element.location(),
336 }),
337 }
338 }
339 }
340 }
341
342 if !nullable(&pattern, definitions, registry) {
343 errors.push(ValidationError {
344 kind: ValidationErrorKind::MissingContent,
345 path: path.clone(),
346 location: element.location(),
347 });
348 }
349 path.pop();
350}
351
352fn param_pairs(params: &[Param]) -> Vec<(&str, &str)> {
353 params
354 .iter()
355 .map(|param| (param.name.as_str(), param.value.as_str()))
356 .collect()
357}
358
359fn schema_value_context(namespace: &Option<String>, schema_context: &Context) -> DatatypeContext {
367 let mut bindings: Vec<(String, String)> = schema_context
368 .namespaces
369 .iter()
370 .map(|(prefix, uri)| (prefix.clone(), uri.clone()))
371 .collect();
372 bindings.push((String::new(), namespace.clone().unwrap_or_default()));
373 DatatypeContext::from_bindings(bindings)
374}
375
376fn check_datatypes_supported(
383 pattern: &Pattern,
384 definitions: &Definitions,
385 registry: &DatatypeRegistry,
386 checked_refs: &mut BTreeSet<String>,
387) -> Result<(), DatatypeError> {
388 match pattern {
389 Pattern::Data {
390 datatype,
391 datatype_library,
392 params,
393 except,
394 ..
395 } => {
396 let library = datatype_library.as_deref().unwrap_or("");
397 let implementation = registry
398 .get(library)
399 .ok_or_else(|| DatatypeError::unsupported_library(library))?;
400 implementation
401 .validate_params(datatype, ¶m_pairs(params))
402 .map_err(|reason| DatatypeError::type_problem(library, datatype, &reason))?;
403 for child in except {
404 check_datatypes_supported(child, definitions, registry, checked_refs)?;
405 }
406 Ok(())
407 }
408 Pattern::Value {
409 datatype,
410 datatype_library,
411 ..
412 } => {
413 let library = datatype_library.as_deref().unwrap_or("");
414 let type_name = datatype.as_deref().unwrap_or("token");
415 let implementation = registry
416 .get(library)
417 .ok_or_else(|| DatatypeError::unsupported_library(library))?;
418 implementation
419 .validate_params(type_name, &[])
420 .map_err(|reason| DatatypeError::type_problem(library, type_name, &reason))
421 }
422 Pattern::Element { body, .. } | Pattern::Attribute { body, .. } => {
423 body.iter().try_for_each(|child| {
424 check_datatypes_supported(child, definitions, registry, checked_refs)
425 })
426 }
427 Pattern::Group(values)
428 | Pattern::Interleave(values)
429 | Pattern::Choice(values)
430 | Pattern::OneOrMore(values)
431 | Pattern::List(values) => values.iter().try_for_each(|child| {
432 check_datatypes_supported(child, definitions, registry, checked_refs)
433 }),
434 Pattern::Ref(name) => {
435 if checked_refs.insert(name.clone())
436 && let Some(target) = definitions.get(name)
437 {
438 check_datatypes_supported(target, definitions, registry, checked_refs)?;
439 }
440 Ok(())
441 }
442 Pattern::Empty | Pattern::NotAllowed | Pattern::Text => Ok(()),
443 Pattern::Optional(_)
444 | Pattern::ZeroOrMore(_)
445 | Pattern::Mixed(_)
446 | Pattern::ExternalRef { .. }
447 | Pattern::ParentRef(_)
448 | Pattern::Grammar(_) => {
449 unreachable!("resolved into other patterns by simplify_pattern")
450 }
451 }
452}
453
454fn resolve_ref<'a>(name: &str, definitions: &'a Definitions) -> &'a Pattern {
461 definitions
462 .get(name)
463 .expect("RELAX NG §4.18 guarantees every ref target exists")
464}
465
466pub(crate) fn nullable(
478 pattern: &Pattern,
479 definitions: &Definitions,
480 registry: &DatatypeRegistry,
481) -> bool {
482 match pattern {
483 Pattern::Empty => true,
484 Pattern::NotAllowed => false,
485 Pattern::Text => true,
486 Pattern::Element { .. } | Pattern::Attribute { .. } => false,
487 Pattern::Data {
488 datatype,
489 datatype_library,
490 params,
491 except,
492 ..
493 } => {
494 let library = datatype_library.as_deref().unwrap_or("");
495 let implementation = registry
496 .get(library)
497 .expect("checked upfront by check_datatypes_supported");
498 implementation.matches(
499 datatype,
500 ¶m_pairs(params),
501 "",
502 &DatatypeContext::empty(),
503 ) && !text_matches_except(except, "", definitions, registry, &DatatypeContext::empty())
504 }
505 Pattern::Value {
506 value,
507 datatype,
508 datatype_library,
509 namespace,
510 context: schema_context,
511 } => {
512 let library = datatype_library.as_deref().unwrap_or("");
513 let type_name = datatype.as_deref().unwrap_or("token");
514 let implementation = registry
515 .get(library)
516 .expect("checked upfront by check_datatypes_supported");
517 implementation.values_equal(
518 type_name,
519 value,
520 &schema_value_context(namespace, schema_context),
521 "",
522 &DatatypeContext::empty(),
523 )
524 }
525 Pattern::List(values) => structurally_nullable(&values[0], definitions),
534 Pattern::Choice(values) => values
535 .iter()
536 .any(|value| nullable(value, definitions, registry)),
537 Pattern::Group(values) | Pattern::Interleave(values) => values
538 .iter()
539 .all(|value| nullable(value, definitions, registry)),
540 Pattern::OneOrMore(values) => nullable(&values[0], definitions, registry),
541 Pattern::Ref(name) => nullable(resolve_ref(name, definitions), definitions, registry),
542 Pattern::Optional(_)
543 | Pattern::ZeroOrMore(_)
544 | Pattern::Mixed(_)
545 | Pattern::ExternalRef { .. }
546 | Pattern::ParentRef(_)
547 | Pattern::Grammar(_) => {
548 unreachable!("resolved into other patterns by simplify_pattern")
549 }
550 }
551}
552
553fn deriv_positions(
565 values: &[Pattern],
566 sequential: bool,
567 wrap: fn(Vec<Pattern>) -> Pattern,
568 definitions: &Definitions,
569 deriv_one: &dyn Fn(&Pattern) -> Pattern,
570) -> Pattern {
571 let mut alternatives = Vec::new();
572 let mut reachable = true;
573 for (index, value) in values.iter().enumerate() {
574 if reachable {
575 let derived = deriv_one(value);
576 if !matches!(derived, Pattern::NotAllowed) {
577 let mut branch = values.to_vec();
578 if sequential {
579 for earlier in branch.iter_mut().take(index) {
592 *earlier = close_child_position(earlier, definitions);
593 }
594 }
595 branch[index] = derived;
596 alternatives.push(wrap(branch));
597 }
598 }
599 if sequential {
600 reachable = reachable && structurally_nullable(value, definitions);
601 }
602 }
603 choice(alternatives)
604}
605
606fn structurally_nullable(pattern: &Pattern, definitions: &Definitions) -> bool {
626 match pattern {
627 Pattern::Attribute { .. } | Pattern::Empty | Pattern::Text => true,
628 Pattern::NotAllowed
629 | Pattern::Element { .. }
630 | Pattern::Data { .. }
631 | Pattern::Value { .. } => false,
632 Pattern::List(values) => structurally_nullable(&values[0], definitions),
633 Pattern::Choice(values) => values
634 .iter()
635 .any(|value| structurally_nullable(value, definitions)),
636 Pattern::Group(values) | Pattern::Interleave(values) => values
637 .iter()
638 .all(|value| structurally_nullable(value, definitions)),
639 Pattern::OneOrMore(values) => structurally_nullable(&values[0], definitions),
640 Pattern::Ref(name) => structurally_nullable(resolve_ref(name, definitions), definitions),
641 Pattern::Optional(_)
642 | Pattern::ZeroOrMore(_)
643 | Pattern::Mixed(_)
644 | Pattern::ExternalRef { .. }
645 | Pattern::ParentRef(_)
646 | Pattern::Grammar(_) => {
647 unreachable!("resolved into other patterns by simplify_pattern")
648 }
649 }
650}
651
652fn wants_text(pattern: &Pattern, definitions: &Definitions) -> bool {
668 match pattern {
669 Pattern::Text | Pattern::Data { .. } | Pattern::Value { .. } | Pattern::List(_) => true,
670 Pattern::Attribute { .. }
671 | Pattern::Empty
672 | Pattern::NotAllowed
673 | Pattern::Element { .. } => false,
674 Pattern::Choice(values) => values.iter().all(|value| wants_text(value, definitions)),
675 Pattern::Interleave(values) => values.iter().any(|value| wants_text(value, definitions)),
676 Pattern::Group(values) => {
677 let mut reachable = true;
678 let mut found = false;
679 for value in values {
680 if reachable {
681 found = found || wants_text(value, definitions);
682 }
683 reachable = reachable && structurally_nullable(value, definitions);
684 }
685 found
686 }
687 Pattern::OneOrMore(values) => wants_text(&values[0], definitions),
688 Pattern::Ref(name) => wants_text(resolve_ref(name, definitions), definitions),
689 Pattern::Optional(_)
690 | Pattern::ZeroOrMore(_)
691 | Pattern::Mixed(_)
692 | Pattern::ExternalRef { .. }
693 | Pattern::ParentRef(_)
694 | Pattern::Grammar(_) => {
695 unreachable!("resolved into other patterns by simplify_pattern")
696 }
697 }
698}
699
700fn close_child_position(pattern: &Pattern, definitions: &Definitions) -> Pattern {
711 match pattern {
712 Pattern::Attribute { .. } => pattern.clone(),
713 Pattern::Group(values) => group(
714 values
715 .iter()
716 .map(|value| close_child_position(value, definitions))
717 .collect(),
718 ),
719 Pattern::Interleave(values) => interleave(
720 values
721 .iter()
722 .map(|value| close_child_position(value, definitions))
723 .collect(),
724 ),
725 Pattern::Choice(values) => choice(
726 values
727 .iter()
728 .map(|value| close_child_position(value, definitions))
729 .collect(),
730 ),
731 Pattern::OneOrMore(values) => close_child_position(&values[0], definitions),
732 Pattern::Ref(name) => close_child_position(resolve_ref(name, definitions), definitions),
733 _ => Pattern::Empty,
734 }
735}
736
737fn deriv_choice(values: &[Pattern], deriv_one: impl Fn(&Pattern) -> Pattern) -> Pattern {
742 choice(values.iter().map(deriv_one).collect())
743}
744
745fn deriv_structural(
757 pattern: &Pattern,
758 group_sequential: bool,
759 definitions: &Definitions,
760 deriv_one: &dyn Fn(&Pattern) -> Pattern,
761) -> Option<Pattern> {
762 Some(match pattern {
763 Pattern::Group(values) => {
764 deriv_positions(values, group_sequential, group, definitions, deriv_one)
765 }
766 Pattern::Interleave(values) => {
767 deriv_positions(values, false, interleave, definitions, deriv_one)
768 }
769 Pattern::Choice(values) => deriv_choice(values, deriv_one),
770 Pattern::OneOrMore(values) => one_or_more_deriv(&values[0], pattern, deriv_one),
771 Pattern::Ref(name) => deriv_one(resolve_ref(name, definitions)),
772 _ => return None,
773 })
774}
775
776fn deriv_attribute(
782 pattern: &Pattern,
783 name: &ExpandedName,
784 value: &str,
785 definitions: &Definitions,
786 registry: &DatatypeRegistry,
787 context: &DatatypeContext,
788) -> Pattern {
789 let deriv_one = |v: &Pattern| deriv_attribute(v, name, value, definitions, registry, context);
790 if let Some(result) = deriv_structural(pattern, false, definitions, &deriv_one) {
791 return result;
792 }
793 match pattern {
794 Pattern::Attribute {
795 name: name_class,
796 body,
797 ..
798 } => {
799 if name_class_matches(name_class, &name.namespace, &name.local)
800 && value_matches(&body[0], value, definitions, registry, context)
801 {
802 Pattern::Empty
803 } else {
804 Pattern::NotAllowed
805 }
806 }
807 Pattern::Element { .. }
808 | Pattern::Text
809 | Pattern::Data { .. }
810 | Pattern::Value { .. }
811 | Pattern::List(_)
812 | Pattern::Empty
813 | Pattern::NotAllowed => Pattern::NotAllowed,
814 Pattern::Optional(_)
815 | Pattern::ZeroOrMore(_)
816 | Pattern::Mixed(_)
817 | Pattern::ExternalRef { .. }
818 | Pattern::ParentRef(_)
819 | Pattern::Grammar(_) => {
820 unreachable!("resolved into other patterns by simplify_pattern")
821 }
822 Pattern::Group(_)
823 | Pattern::Interleave(_)
824 | Pattern::Choice(_)
825 | Pattern::OneOrMore(_)
826 | Pattern::Ref(_) => unreachable!("handled by deriv_structural above"),
827 }
828}
829
830fn deriv_element_event(
834 pattern: &Pattern,
835 name: &ExpandedName,
836 definitions: &Definitions,
837) -> Pattern {
838 let deriv_one = |v: &Pattern| deriv_element_event(v, name, definitions);
839 if let Some(result) = deriv_structural(pattern, true, definitions, &deriv_one) {
840 return result;
841 }
842 match pattern {
843 Pattern::Element {
844 name: name_class, ..
845 } => {
846 if name_class_matches(name_class, &name.namespace, &name.local) {
847 Pattern::Empty
848 } else {
849 Pattern::NotAllowed
850 }
851 }
852 Pattern::Attribute { .. }
853 | Pattern::Text
854 | Pattern::Data { .. }
855 | Pattern::Value { .. }
856 | Pattern::List(_)
857 | Pattern::Empty
858 | Pattern::NotAllowed => Pattern::NotAllowed,
859 Pattern::Optional(_)
860 | Pattern::ZeroOrMore(_)
861 | Pattern::Mixed(_)
862 | Pattern::ExternalRef { .. }
863 | Pattern::ParentRef(_)
864 | Pattern::Grammar(_) => {
865 unreachable!("resolved into other patterns by simplify_pattern")
866 }
867 Pattern::Group(_)
868 | Pattern::Interleave(_)
869 | Pattern::Choice(_)
870 | Pattern::OneOrMore(_)
871 | Pattern::Ref(_) => unreachable!("handled by deriv_structural above"),
872 }
873}
874
875fn deriv_text(
880 pattern: &Pattern,
881 text: &str,
882 definitions: &Definitions,
883 registry: &DatatypeRegistry,
884 context: &DatatypeContext,
885) -> Pattern {
886 let deriv_one = |v: &Pattern| deriv_text(v, text, definitions, registry, context);
887 if let Some(result) = deriv_structural(pattern, true, definitions, &deriv_one) {
888 return result;
889 }
890 match pattern {
891 Pattern::Text => Pattern::Text,
895 Pattern::Data {
896 datatype,
897 datatype_library,
898 params,
899 except,
900 ..
901 } => {
902 let library = datatype_library.as_deref().unwrap_or("");
903 let implementation = registry
904 .get(library)
905 .expect("checked upfront by check_datatypes_supported");
906 if implementation.matches(datatype, ¶m_pairs(params), text, context)
907 && !text_matches_except(except, text, definitions, registry, context)
908 {
909 Pattern::Empty
910 } else {
911 Pattern::NotAllowed
912 }
913 }
914 Pattern::Value {
915 value,
916 datatype,
917 datatype_library,
918 namespace,
919 context: schema_context,
920 } => {
921 let library = datatype_library.as_deref().unwrap_or("");
922 let type_name = datatype.as_deref().unwrap_or("token");
923 let implementation = registry
924 .get(library)
925 .expect("checked upfront by check_datatypes_supported");
926 if implementation.values_equal(
927 type_name,
928 value,
929 &schema_value_context(namespace, schema_context),
930 text,
931 context,
932 ) {
933 Pattern::Empty
934 } else {
935 Pattern::NotAllowed
936 }
937 }
938 Pattern::List(values) => {
939 if list_matches(&values[0], text, definitions, registry, context) {
940 Pattern::Empty
941 } else {
942 Pattern::NotAllowed
943 }
944 }
945 Pattern::Element { .. }
946 | Pattern::Attribute { .. }
947 | Pattern::Empty
948 | Pattern::NotAllowed => Pattern::NotAllowed,
949 Pattern::Optional(_)
950 | Pattern::ZeroOrMore(_)
951 | Pattern::Mixed(_)
952 | Pattern::ExternalRef { .. }
953 | Pattern::ParentRef(_)
954 | Pattern::Grammar(_) => {
955 unreachable!("resolved into other patterns by simplify_pattern")
956 }
957 Pattern::Group(_)
958 | Pattern::Interleave(_)
959 | Pattern::Choice(_)
960 | Pattern::OneOrMore(_)
961 | Pattern::Ref(_) => unreachable!("handled by deriv_structural above"),
962 }
963}
964
965fn one_or_more_deriv(
969 inner: &Pattern,
970 whole: &Pattern,
971 deriv_one: &dyn Fn(&Pattern) -> Pattern,
972) -> Pattern {
973 group(vec![
974 deriv_one(inner),
975 choice(vec![whole.clone(), Pattern::Empty]),
976 ])
977}
978
979fn text_matches_except(
980 except: &[Pattern],
981 text: &str,
982 definitions: &Definitions,
983 registry: &DatatypeRegistry,
984 context: &DatatypeContext,
985) -> bool {
986 except.iter().any(|item| {
987 nullable(
988 &deriv_text(item, text, definitions, registry, context),
989 definitions,
990 registry,
991 )
992 })
993}
994
995fn list_matches(
998 pattern: &Pattern,
999 text: &str,
1000 definitions: &Definitions,
1001 registry: &DatatypeRegistry,
1002 context: &DatatypeContext,
1003) -> bool {
1004 let mut current = pattern.clone();
1005 for token in text.split_ascii_whitespace() {
1006 current = deriv_text(¤t, token, definitions, registry, context);
1007 if matches!(current, Pattern::NotAllowed) {
1008 return false;
1009 }
1010 }
1011 structurally_nullable(¤t, definitions)
1017}
1018
1019fn value_matches(
1025 pattern: &Pattern,
1026 value: &str,
1027 definitions: &Definitions,
1028 registry: &DatatypeRegistry,
1029 context: &DatatypeContext,
1030) -> bool {
1031 let derived = deriv_text(pattern, value, definitions, registry, context);
1032 if !matches!(derived, Pattern::NotAllowed) && structurally_nullable(&derived, definitions) {
1033 return true;
1034 }
1035 value.trim().is_empty() && !wants_text(pattern, definitions)
1036}
1037
1038fn attribute_name_known(pattern: &Pattern, name: &ExpandedName, definitions: &Definitions) -> bool {
1045 match pattern {
1046 Pattern::Attribute {
1047 name: name_class, ..
1048 } => name_class_matches(name_class, &name.namespace, &name.local),
1049 Pattern::Group(values) | Pattern::Interleave(values) | Pattern::Choice(values) => values
1050 .iter()
1051 .any(|value| attribute_name_known(value, name, definitions)),
1052 Pattern::OneOrMore(values) => attribute_name_known(&values[0], name, definitions),
1053 Pattern::Ref(ref_name) => {
1054 attribute_name_known(resolve_ref(ref_name, definitions), name, definitions)
1055 }
1056 _ => false,
1057 }
1058}
1059
1060fn find_element_contents(
1068 pattern: &Pattern,
1069 name: &ExpandedName,
1070 definitions: &Definitions,
1071) -> Option<Pattern> {
1072 let mut found = Vec::new();
1073 collect_element_contents(pattern, name, definitions, &mut found);
1074 if found.is_empty() {
1075 None
1076 } else {
1077 Some(choice(found))
1078 }
1079}
1080
1081fn collect_element_contents(
1082 pattern: &Pattern,
1083 name: &ExpandedName,
1084 definitions: &Definitions,
1085 out: &mut Vec<Pattern>,
1086) {
1087 match pattern {
1088 Pattern::Element {
1089 name: name_class,
1090 body,
1091 ..
1092 } if name_class_matches(name_class, &name.namespace, &name.local) => {
1093 out.push(body[0].clone());
1094 }
1095 Pattern::Choice(values) | Pattern::Interleave(values) => {
1096 for value in values {
1097 collect_element_contents(value, name, definitions, out);
1098 }
1099 }
1100 Pattern::Group(values) => {
1101 let mut reachable = true;
1102 for value in values {
1103 if !reachable {
1104 break;
1105 }
1106 collect_element_contents(value, name, definitions, out);
1107 reachable = structurally_nullable(value, definitions);
1108 }
1109 }
1110 Pattern::OneOrMore(values) => collect_element_contents(&values[0], name, definitions, out),
1111 Pattern::Ref(ref_name) => {
1112 collect_element_contents(resolve_ref(ref_name, definitions), name, definitions, out)
1113 }
1114 _ => {}
1115 }
1116}
1117
1118#[cfg(test)]
1119mod tests {
1120 use super::*;
1121 use crate::datatypes::DatatypeLibrary;
1122 use crate::{ResolveError, SchemaResolver, SchemaSource, SchemaSyntax};
1123
1124 struct NoResolver;
1125
1126 impl SchemaResolver for NoResolver {
1127 fn resolve(&self, href: &str, _base_uri: &str) -> Result<SchemaSource, ResolveError> {
1128 Err(ResolveError::new(format!(
1129 "validator tests don't use includes (requested `{href}`)"
1130 )))
1131 }
1132 }
1133
1134 fn schema(text: &str) -> CompiledSchema {
1135 let document = crate::parse(&SchemaSource::new(
1136 text,
1137 "memory:/main.rnc",
1138 SchemaSyntax::Compact,
1139 ))
1140 .expect("schema parses");
1141 crate::simplify(&document, &NoResolver).expect("schema simplifies")
1142 }
1143
1144 fn xml_schema(text: &str) -> CompiledSchema {
1145 let document = crate::parse(&SchemaSource::new(
1146 text,
1147 "memory:/main.rng",
1148 SchemaSyntax::Xml,
1149 ))
1150 .expect("schema parses");
1151 crate::simplify(&document, &NoResolver).expect("schema simplifies")
1152 }
1153
1154 fn registry() -> DatatypeRegistry {
1155 DatatypeRegistry::new()
1156 }
1157
1158 #[derive(Clone, Debug, Eq, PartialEq)]
1163 struct TestLocation {
1164 line: u32,
1165 column: u32,
1166 }
1167
1168 impl fmt::Display for TestLocation {
1169 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1170 write!(formatter, "{}:{}", self.line, self.column)
1171 }
1172 }
1173
1174 #[derive(Clone, Debug)]
1175 struct TestElement {
1176 name: ExpandedName,
1177 attributes: Vec<(ExpandedName, String)>,
1178 children: Vec<Content<TestElement>>,
1179 namespace_bindings: Vec<(String, String)>,
1180 location: Option<TestLocation>,
1181 }
1182
1183 impl TestElement {
1184 fn new(local: &str) -> Self {
1185 Self {
1186 name: ExpandedName::new(None::<String>, local),
1187 attributes: Vec::new(),
1188 children: Vec::new(),
1189 namespace_bindings: Vec::new(),
1190 location: None,
1191 }
1192 }
1193
1194 fn new_namespaced(namespace: &str, local: &str) -> Self {
1195 Self {
1196 name: ExpandedName::new(Some(namespace), local),
1197 attributes: Vec::new(),
1198 children: Vec::new(),
1199 namespace_bindings: Vec::new(),
1200 location: None,
1201 }
1202 }
1203
1204 fn at(mut self, line: u32, column: u32) -> Self {
1205 self.location = Some(TestLocation { line, column });
1206 self
1207 }
1208
1209 fn attr(mut self, local: &str, value: &str) -> Self {
1210 self.attributes
1211 .push((ExpandedName::new(None::<String>, local), value.to_owned()));
1212 self
1213 }
1214
1215 fn text(mut self, text: &str) -> Self {
1216 self.children.push(Content::Text(text.to_owned()));
1217 self
1218 }
1219
1220 fn child(mut self, element: TestElement) -> Self {
1221 self.children.push(Content::Element(element));
1222 self
1223 }
1224
1225 fn xmlns(mut self, prefix: &str, uri: &str) -> Self {
1226 self.namespace_bindings
1227 .push((prefix.to_owned(), uri.to_owned()));
1228 self
1229 }
1230 }
1231
1232 impl Element for TestElement {
1233 type Location = TestLocation;
1234 fn name(&self) -> ExpandedName {
1235 self.name.clone()
1236 }
1237 fn attributes(&self) -> impl Iterator<Item = (ExpandedName, String)> {
1238 self.attributes.clone().into_iter()
1239 }
1240 fn children(&self) -> impl Iterator<Item = Content<Self>> {
1241 self.children.clone().into_iter()
1242 }
1243 fn location(&self) -> Option<Self::Location> {
1244 self.location.clone()
1245 }
1246 fn namespace_bindings(&self) -> impl Iterator<Item = (String, String)> {
1247 self.namespace_bindings.clone().into_iter()
1248 }
1249 }
1250
1251 fn kinds(errors: &[ValidationError<TestLocation>]) -> Vec<ValidationErrorKind> {
1252 errors.iter().map(|error| error.kind().clone()).collect()
1253 }
1254
1255 fn name(local: &str) -> ExpandedName {
1256 ExpandedName::new(None::<String>, local)
1257 }
1258
1259 #[test]
1262 fn nullable_base_cases() {
1263 let definitions = Definitions::new();
1264 let registry = registry();
1265 assert!(nullable(&Pattern::Empty, &definitions, ®istry));
1266 assert!(!nullable(&Pattern::NotAllowed, &definitions, ®istry));
1267 assert!(nullable(&Pattern::Text, &definitions, ®istry));
1268 }
1269
1270 #[test]
1271 fn nullable_group_requires_every_operand_nullable() {
1272 let definitions = Definitions::new();
1273 let registry = registry();
1274 assert!(nullable(
1275 &Pattern::Group(vec![Pattern::Empty, Pattern::Text]),
1276 &definitions,
1277 ®istry
1278 ));
1279 assert!(!nullable(
1280 &Pattern::Group(vec![Pattern::Empty, Pattern::NotAllowed]),
1281 &definitions,
1282 ®istry
1283 ));
1284 }
1285
1286 #[test]
1287 fn nullable_choice_requires_any_operand_nullable() {
1288 let definitions = Definitions::new();
1289 let registry = registry();
1290 assert!(nullable(
1291 &Pattern::Choice(vec![Pattern::NotAllowed, Pattern::Empty]),
1292 &definitions,
1293 ®istry
1294 ));
1295 assert!(!nullable(
1296 &Pattern::Choice(vec![Pattern::NotAllowed, Pattern::NotAllowed]),
1297 &definitions,
1298 ®istry
1299 ));
1300 }
1301
1302 #[test]
1303 fn nullable_one_or_more_delegates_to_inner() {
1304 let definitions = Definitions::new();
1305 let registry = registry();
1306 assert!(nullable(
1307 &Pattern::OneOrMore(vec![Pattern::Empty]),
1308 &definitions,
1309 ®istry
1310 ));
1311 assert!(!nullable(
1312 &Pattern::OneOrMore(vec![Pattern::NotAllowed]),
1313 &definitions,
1314 ®istry
1315 ));
1316 }
1317
1318 #[test]
1319 fn nullable_list_delegates_to_inner_same_as_zero_tokens_would() {
1320 let definitions = Definitions::new();
1321 let registry = registry();
1322 assert!(nullable(
1324 &Pattern::List(vec![Pattern::Empty]),
1325 &definitions,
1326 ®istry
1327 ));
1328 assert!(!nullable(
1329 &Pattern::List(vec![Pattern::NotAllowed]),
1330 &definitions,
1331 ®istry
1332 ));
1333 }
1334
1335 #[test]
1336 fn nullable_ref_resolves_through_definitions() {
1337 let definitions = Definitions::from([("x".to_owned(), Pattern::Empty)]);
1338 let registry = registry();
1339 assert!(nullable(&Pattern::Ref("x".into()), &definitions, ®istry));
1340 }
1341
1342 #[test]
1345 fn deriv_element_event_matches_by_name_and_rejects_others() {
1346 let compiled = schema("start = element foo { empty }");
1347 let registry = registry();
1348 assert!(nullable(
1349 &deriv_element_event(&compiled.start, &name("foo"), &compiled.definitions),
1350 &compiled.definitions,
1351 ®istry
1352 ));
1353 assert!(matches!(
1354 deriv_element_event(&compiled.start, &name("bar"), &compiled.definitions),
1355 Pattern::NotAllowed
1356 ));
1357 }
1358
1359 #[test]
1360 fn deriv_attribute_checks_name_and_value() {
1361 let compiled = schema(r#"start = element foo { attribute id { "x" } }"#);
1362 let registry = registry();
1363 let context = DatatypeContext::empty();
1364 let Pattern::Element { body, .. } = &compiled.start else {
1365 panic!("expected an element pattern");
1366 };
1367 assert!(nullable(
1368 &deriv_attribute(
1369 &body[0],
1370 &name("id"),
1371 "x",
1372 &compiled.definitions,
1373 ®istry,
1374 &context
1375 ),
1376 &compiled.definitions,
1377 ®istry
1378 ));
1379 assert!(matches!(
1380 deriv_attribute(
1381 &body[0],
1382 &name("id"),
1383 "y",
1384 &compiled.definitions,
1385 ®istry,
1386 &context
1387 ),
1388 Pattern::NotAllowed
1389 ));
1390 assert!(matches!(
1391 deriv_attribute(
1392 &body[0],
1393 &name("other"),
1394 "x",
1395 &compiled.definitions,
1396 ®istry,
1397 &context
1398 ),
1399 Pattern::NotAllowed
1400 ));
1401 }
1402
1403 #[test]
1404 fn deriv_text_data_accepts_any_string_value_requires_exact_match() {
1405 let registry = registry();
1406 let context = DatatypeContext::empty();
1407 let compiled = schema("start = element foo { token }");
1408 let Pattern::Element { body, .. } = &compiled.start else {
1409 panic!("expected an element pattern");
1410 };
1411 assert!(nullable(
1412 &deriv_text(
1413 &body[0],
1414 "whatever text",
1415 &compiled.definitions,
1416 ®istry,
1417 &context
1418 ),
1419 &compiled.definitions,
1420 ®istry
1421 ));
1422
1423 let compiled = schema(r#"start = element foo { "exact" }"#);
1424 let Pattern::Element { body, .. } = &compiled.start else {
1425 panic!("expected an element pattern");
1426 };
1427 assert!(nullable(
1428 &deriv_text(
1429 &body[0],
1430 "exact",
1431 &compiled.definitions,
1432 ®istry,
1433 &context
1434 ),
1435 &compiled.definitions,
1436 ®istry
1437 ));
1438 assert!(matches!(
1439 deriv_text(
1440 &body[0],
1441 "different",
1442 &compiled.definitions,
1443 ®istry,
1444 &context
1445 ),
1446 Pattern::NotAllowed
1447 ));
1448 }
1449
1450 #[test]
1451 fn deriv_text_token_equality_collapses_whitespace() {
1452 let registry = registry();
1453 let context = DatatypeContext::empty();
1454 let compiled = schema(r#"start = element foo { token "a b" }"#);
1455 let Pattern::Element { body, .. } = &compiled.start else {
1456 panic!("expected an element pattern");
1457 };
1458 assert!(nullable(
1459 &deriv_text(
1460 &body[0],
1461 "a b",
1462 &compiled.definitions,
1463 ®istry,
1464 &context
1465 ),
1466 &compiled.definitions,
1467 ®istry
1468 ));
1469 }
1470
1471 #[test]
1474 fn group_derivative_is_sequential() {
1475 let registry = registry();
1476 let compiled = schema("start = element root { element a { empty }, element b { empty } }");
1477 let Pattern::Element { body, .. } = &compiled.start else {
1478 panic!("expected an element pattern");
1479 };
1480 let content = &body[0];
1481 assert!(matches!(
1483 deriv_element_event(content, &name("b"), &compiled.definitions),
1484 Pattern::NotAllowed
1485 ));
1486 let after_a = deriv_element_event(content, &name("a"), &compiled.definitions);
1487 let after_a_b = deriv_element_event(&after_a, &name("b"), &compiled.definitions);
1488 assert!(nullable(&after_a_b, &compiled.definitions, ®istry));
1489 }
1490
1491 #[test]
1492 fn interleave_derivative_allows_either_order() {
1493 let registry = registry();
1494 let compiled = schema("start = element root { element a { empty } & element b { empty } }");
1495 let Pattern::Element { body, .. } = &compiled.start else {
1496 panic!("expected an element pattern");
1497 };
1498 let content = &body[0];
1499 let after_b = deriv_element_event(content, &name("b"), &compiled.definitions);
1501 assert!(!matches!(after_b, Pattern::NotAllowed));
1502 let after_b_a = deriv_element_event(&after_b, &name("a"), &compiled.definitions);
1503 assert!(nullable(&after_b_a, &compiled.definitions, ®istry));
1504 }
1505
1506 #[test]
1510 fn attribute_matches_through_ref_and_interleave_xmloxide_issue_56() {
1511 let compiled = schema(
1512 "start = element foo { attrs & text }\n\
1513 attrs = attribute id { text }",
1514 );
1515 let document = TestElement::new("foo").attr("id", "x").text("hello");
1516 let errors = validate(&compiled, ®istry(), &document).expect("builtin datatypes only");
1517 assert_eq!(kinds(&errors), Vec::<ValidationErrorKind>::new());
1518 }
1519
1520 #[test]
1521 fn attribute_name_known_resolves_ref_through_group_and_choice_too() {
1522 let compiled = schema(
1525 "start = element foo { a, b }\n\
1526 a = attribute x { text } | attribute y { text }\n\
1527 b = empty",
1528 );
1529 let Pattern::Element { body, .. } = &compiled.start else {
1530 panic!("expected an element pattern");
1531 };
1532 assert!(attribute_name_known(
1533 &body[0],
1534 &name("x"),
1535 &compiled.definitions
1536 ));
1537 assert!(attribute_name_known(
1538 &body[0],
1539 &name("y"),
1540 &compiled.definitions
1541 ));
1542 assert!(!attribute_name_known(
1543 &body[0],
1544 &name("z"),
1545 &compiled.definitions
1546 ));
1547 }
1548
1549 #[test]
1550 fn ref_is_transparent_for_child_elements_through_choice_and_group() {
1551 let compiled = schema(
1552 "start = element foo { greeting, farewell }\n\
1553 greeting = element hello { empty } | element hi { empty }\n\
1554 farewell = element bye { empty }",
1555 );
1556 let document = TestElement::new("foo")
1557 .child(TestElement::new("hi"))
1558 .child(TestElement::new("bye"));
1559 let errors = validate(&compiled, ®istry(), &document).expect("builtin datatypes only");
1560 assert_eq!(kinds(&errors), Vec::<ValidationErrorKind>::new());
1561 }
1562
1563 #[test]
1566 fn valid_document_with_required_attribute_and_text() {
1567 let compiled = schema("start = element foo { attribute id { text }, text }");
1568 let document = TestElement::new("foo").attr("id", "1").text("hello");
1569 let errors = validate(&compiled, ®istry(), &document).unwrap();
1570 assert!(errors.is_empty());
1571 }
1572
1573 #[test]
1574 fn missing_required_attribute_is_reported() {
1575 let compiled = schema("start = element foo { attribute id { text }, text }");
1576 let document = TestElement::new("foo").text("hello");
1577 let errors = validate(&compiled, ®istry(), &document).unwrap();
1578 assert_eq!(kinds(&errors), vec![ValidationErrorKind::MissingContent]);
1579 }
1580
1581 #[test]
1582 fn unexpected_attribute_is_reported() {
1583 let compiled = schema("start = element foo { empty }");
1584 let document = TestElement::new("foo").attr("id", "1");
1585 let errors = validate(&compiled, ®istry(), &document).unwrap();
1586 assert_eq!(
1587 kinds(&errors),
1588 vec![ValidationErrorKind::UnexpectedAttribute(name("id"))]
1589 );
1590 }
1591
1592 #[test]
1598 fn structured_location_is_reported_on_the_validation_error() {
1599 let compiled = schema("start = element foo { empty }");
1600 let document = TestElement::new("foo").attr("id", "1").at(3, 7);
1601 let errors = validate(&compiled, ®istry(), &document).unwrap();
1602 assert_eq!(errors.len(), 1);
1603 assert_eq!(
1604 errors[0].location(),
1605 Some(&TestLocation { line: 3, column: 7 })
1606 );
1607 assert_eq!(errors[0].to_string(), "unexpected attribute `id` at 3:7");
1610 }
1611
1612 #[test]
1613 fn invalid_attribute_value_is_reported_distinctly_from_unexpected_attribute() {
1614 let compiled = schema(r#"start = element foo { attribute id { "x" } }"#);
1615 let document = TestElement::new("foo").attr("id", "y");
1616 let errors = validate(&compiled, ®istry(), &document).unwrap();
1617 assert_eq!(
1622 kinds(&errors),
1623 vec![
1624 ValidationErrorKind::InvalidAttributeValue(name("id"), "y".to_owned()),
1625 ValidationErrorKind::MissingContent,
1626 ]
1627 );
1628 }
1629
1630 #[test]
1631 fn unexpected_child_element_is_reported() {
1632 let compiled = schema("start = element foo { element bar { empty } }");
1633 let document = TestElement::new("foo").child(TestElement::new("baz"));
1634 let errors = validate(&compiled, ®istry(), &document).unwrap();
1635 assert_eq!(
1636 kinds(&errors),
1637 vec![
1638 ValidationErrorKind::UnexpectedElement(name("baz")),
1639 ValidationErrorKind::MissingContent,
1640 ]
1641 );
1642 }
1643
1644 #[test]
1645 fn unexpected_root_element_is_reported() {
1646 let compiled = schema("start = element foo { empty }");
1647 let document = TestElement::new("bar");
1648 let errors = validate(&compiled, ®istry(), &document).unwrap();
1649 assert_eq!(
1650 kinds(&errors),
1651 vec![ValidationErrorKind::UnexpectedElement(name("bar"))]
1652 );
1653 }
1654
1655 #[test]
1656 fn unexpected_text_is_reported() {
1657 let compiled = schema("start = element foo { empty }");
1658 let document = TestElement::new("foo").text("surprise");
1659 let errors = validate(&compiled, ®istry(), &document).unwrap();
1660 assert_eq!(kinds(&errors), vec![ValidationErrorKind::UnexpectedText]);
1661 }
1662
1663 #[test]
1664 fn one_or_more_children_are_validated_recursively_and_repeatedly() {
1665 let compiled = schema("start = element foo { element item { text }+ }");
1666 let document = TestElement::new("foo")
1667 .child(TestElement::new("item").text("a"))
1668 .child(TestElement::new("item").text("b"));
1669 let errors = validate(&compiled, ®istry(), &document).unwrap();
1670 assert!(errors.is_empty());
1671 }
1672
1673 #[test]
1674 fn one_or_more_requires_at_least_one_occurrence() {
1675 let compiled = schema("start = element foo { element item { empty }+ }");
1676 let document = TestElement::new("foo");
1677 let errors = validate(&compiled, ®istry(), &document).unwrap();
1678 assert_eq!(kinds(&errors), vec![ValidationErrorKind::MissingContent]);
1679 }
1680
1681 #[test]
1682 fn interleaved_text_and_element_children_validate_regardless_of_order() {
1683 let compiled = schema("start = element foo { mixed { element bar { empty } } }");
1684 let document = TestElement::new("foo")
1685 .text("before ")
1686 .child(TestElement::new("bar"))
1687 .text(" after");
1688 let errors = validate(&compiled, ®istry(), &document).unwrap();
1689 assert!(errors.is_empty());
1690 }
1691
1692 #[test]
1693 fn list_datatype_matches_whitespace_separated_tokens() {
1694 let compiled = schema("start = element foo { list { token+ } }");
1695 let document = TestElement::new("foo").text("a b c");
1696 let errors = validate(&compiled, ®istry(), &document).unwrap();
1697 assert!(errors.is_empty());
1698 }
1699
1700 #[test]
1701 fn list_datatype_rejects_a_token_that_does_not_match() {
1702 let compiled = schema(r#"start = element foo { list { "a", "b" } }"#);
1703 let document = TestElement::new("foo").text("a c");
1704 let errors = validate(&compiled, ®istry(), &document).unwrap();
1705 assert_eq!(
1706 kinds(&errors),
1707 vec![
1708 ValidationErrorKind::UnexpectedText,
1709 ValidationErrorKind::MissingContent,
1710 ]
1711 );
1712 }
1713
1714 #[test]
1715 fn unsupported_datatype_library_fails_fast_before_validating() {
1716 let compiled = xml_schema(
1724 r#"<element xmlns="http://relaxng.org/ns/structure/1.0" name="foo">
1725 <data type="non-empty-string" datatypeLibrary="http://whattf.org/datatype-draft"/>
1726 </element>"#,
1727 );
1728 let document = TestElement::new("foo").text("hello");
1729 let error = validate(&compiled, ®istry(), &document).unwrap_err();
1730 assert_eq!(error.library(), "http://whattf.org/datatype-draft");
1731 }
1732
1733 #[test]
1734 fn builtin_only_registry_rejects_xsd_too() {
1735 let compiled = schema(
1736 "datatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\
1737 start = element foo { xsd:string }",
1738 );
1739 let document = TestElement::new("foo").text("hello");
1740 let error = validate(&compiled, &DatatypeRegistry::builtin_only(), &document).unwrap_err();
1741 assert_eq!(
1742 error.library(),
1743 "http://www.w3.org/2001/XMLSchema-datatypes"
1744 );
1745 }
1746
1747 #[test]
1750 fn xsd_string_and_integer_types_match() {
1751 let compiled = xml_schema(
1756 r#"<element xmlns="http://relaxng.org/ns/structure/1.0" name="foo" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
1757 <group>
1758 <attribute name="count"><data type="positiveInteger"/></attribute>
1759 <data type="string"/>
1760 </group>
1761 </element>"#,
1762 );
1763 let valid = TestElement::new("foo").attr("count", "3").text("hello");
1764 assert!(validate(&compiled, ®istry(), &valid).unwrap().is_empty());
1765
1766 let invalid = TestElement::new("foo").attr("count", "0").text("hello");
1767 let errors = validate(&compiled, ®istry(), &invalid).unwrap();
1768 assert_eq!(
1769 kinds(&errors),
1770 vec![
1771 ValidationErrorKind::InvalidAttributeValue(name("count"), "0".to_owned()),
1772 ValidationErrorKind::MissingContent,
1773 ]
1774 );
1775 }
1776
1777 #[test]
1778 fn xsd_pattern_facet_is_enforced() {
1779 let compiled = xml_schema(
1780 r#"<element xmlns="http://relaxng.org/ns/structure/1.0" name="foo" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
1781 <data type="string"><param name="pattern">[a-z]+</param></data>
1782 </element>"#,
1783 );
1784 let valid = TestElement::new("foo").text("abc");
1785 assert!(validate(&compiled, ®istry(), &valid).unwrap().is_empty());
1786 let invalid = TestElement::new("foo").text("ABC");
1787 assert_eq!(
1788 kinds(&validate(&compiled, ®istry(), &invalid).unwrap()),
1789 vec![
1790 ValidationErrorKind::UnexpectedText,
1791 ValidationErrorKind::MissingContent,
1792 ]
1793 );
1794 }
1795
1796 #[test]
1797 fn xsd_decimal_value_equality_ignores_lexical_differences() {
1798 let compiled = xml_schema(
1799 r#"<element xmlns="http://relaxng.org/ns/structure/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema-datatypes" name="foo">
1800 <value type="decimal" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">1.50</value>
1801 </element>"#,
1802 );
1803 let document = TestElement::new("foo").text("01.500");
1806 assert!(
1807 validate(&compiled, ®istry(), &document)
1808 .unwrap()
1809 .is_empty()
1810 );
1811 }
1812
1813 #[test]
1814 fn xsd_qname_resolves_prefix_via_document_namespace_bindings() {
1815 let compiled = xml_schema(
1816 r#"<element xmlns="http://relaxng.org/ns/structure/1.0" name="foo">
1817 <data type="QName" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
1818 </element>"#,
1819 );
1820 let document = TestElement::new("foo")
1821 .xmlns("h", "urn:example:h")
1822 .text("h:widget");
1823 assert!(
1824 validate(&compiled, ®istry(), &document)
1825 .unwrap()
1826 .is_empty()
1827 );
1828
1829 let unresolved = TestElement::new("foo").text("h:widget");
1833 assert_eq!(
1834 kinds(&validate(&compiled, ®istry(), &unresolved).unwrap()),
1835 vec![
1836 ValidationErrorKind::UnexpectedText,
1837 ValidationErrorKind::MissingContent,
1838 ]
1839 );
1840 }
1841
1842 #[test]
1843 fn xsd_unknown_type_name_is_a_datatype_error() {
1844 let compiled = xml_schema(
1845 r#"<element xmlns="http://relaxng.org/ns/structure/1.0" name="foo" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
1846 <data type="not-a-real-type"/>
1847 </element>"#,
1848 );
1849 let document = TestElement::new("foo").text("hello");
1850 let error = validate(&compiled, ®istry(), &document).unwrap_err();
1851 assert_eq!(
1852 error.library(),
1853 "http://www.w3.org/2001/XMLSchema-datatypes"
1854 );
1855 }
1856
1857 struct DummyNonEmptyStringLibrary;
1862
1863 impl DatatypeLibrary for DummyNonEmptyStringLibrary {
1864 fn validate_params(&self, type_name: &str, params: &[(&str, &str)]) -> Result<(), String> {
1865 if type_name != "non-empty-string" {
1866 return Err(format!("unknown dummy type `{type_name}`"));
1867 }
1868 if let Some((param, _)) = params.first() {
1869 return Err(format!(
1870 "`non-empty-string` takes no parameters, found `{param}`"
1871 ));
1872 }
1873 Ok(())
1874 }
1875
1876 fn matches(
1877 &self,
1878 type_name: &str,
1879 _params: &[(&str, &str)],
1880 value: &str,
1881 _context: &DatatypeContext,
1882 ) -> bool {
1883 type_name == "non-empty-string" && !value.is_empty()
1884 }
1885
1886 fn values_equal(
1887 &self,
1888 type_name: &str,
1889 expected: &str,
1890 _expected_context: &DatatypeContext,
1891 actual: &str,
1892 _actual_context: &DatatypeContext,
1893 ) -> bool {
1894 type_name == "non-empty-string" && expected == actual
1895 }
1896 }
1897
1898 #[test]
1899 fn external_plugin_library_can_be_registered_and_used() {
1900 let compiled = xml_schema(
1901 r#"<element xmlns="http://relaxng.org/ns/structure/1.0" name="foo">
1902 <data type="non-empty-string" datatypeLibrary="http://whattf.org/datatype-draft"/>
1903 </element>"#,
1904 );
1905 let mut registry = DatatypeRegistry::new();
1906 registry.register(
1907 "http://whattf.org/datatype-draft",
1908 DummyNonEmptyStringLibrary,
1909 );
1910
1911 let valid = TestElement::new("foo").text("hello");
1912 assert!(validate(&compiled, ®istry, &valid).unwrap().is_empty());
1913
1914 let invalid = TestElement::new("foo").text("");
1922 assert_eq!(
1923 kinds(&validate(&compiled, ®istry, &invalid).unwrap()),
1924 vec![
1925 ValidationErrorKind::UnexpectedText,
1926 ValidationErrorKind::MissingContent,
1927 ]
1928 );
1929 }
1930
1931 #[test]
1940 fn adapter_smoke_test_with_a_realistic_xhtml_shaped_tree() {
1941 const XHTML: &str = "http://www.w3.org/1999/xhtml";
1942 let compiled = xml_schema(
1948 r#"<element xmlns="http://relaxng.org/ns/structure/1.0" xmlns:xhtml="http://www.w3.org/1999/xhtml" name="xhtml:html">
1949 <group>
1950 <element name="xhtml:head"><empty/></element>
1951 <element name="xhtml:body">
1952 <element name="xhtml:p">
1953 <group>
1954 <attribute name="id"><text/></attribute>
1955 <text/>
1956 </group>
1957 </element>
1958 </element>
1959 </group>
1960 </element>"#,
1961 );
1962 let document = TestElement::new_namespaced(XHTML, "html")
1963 .child(TestElement::new_namespaced(XHTML, "head"))
1964 .child(
1965 TestElement::new_namespaced(XHTML, "body").child(
1966 TestElement::new_namespaced(XHTML, "p")
1967 .attr("id", "intro")
1968 .text("hello"),
1969 ),
1970 );
1971 let errors = validate(&compiled, ®istry(), &document).expect("builtin datatypes only");
1972 assert_eq!(kinds(&errors), Vec::<ValidationErrorKind>::new());
1973 }
1974}