1use std::sync::Arc;
21
22use rustc_hash::FxHashMap as HashMap;
23
24use crate::reader::{Attr, EventInto, XmlReader};
25
26use super::error::{ValidationError, ValidationIssue, ValidationKind, ValidationOptions};
27
28mod walker;
29pub(crate) use walker::DocumentEventSource;
30
31pub(crate) trait XsdEventSource<'x> {
39 fn next_into(&mut self, attr_buf: &mut Vec<Attr<'x>>) -> crate::error::Result<EventInto<'x>>;
40 fn last_start_offset(&self) -> Option<usize>;
44 fn src_offset(&self) -> usize;
46 fn line_col_at(&self, offset: usize) -> (u32, u32);
49 fn fill_default_attr(&self, _name: &str, _value: &str) {}
54 fn current_node_key(&self) -> Option<usize> { None }
61}
62
63impl<'x> XsdEventSource<'x> for XmlReader<'x> {
64 #[inline] fn next_into(&mut self, buf: &mut Vec<Attr<'x>>) -> crate::error::Result<EventInto<'x>> {
65 XmlReader::next_into(self, buf)
66 }
67 #[inline] fn last_start_offset(&self) -> Option<usize> {
68 XmlReader::last_start_offset(self)
69 }
70 #[inline] fn src_offset(&self) -> usize {
71 XmlReader::src_offset(self)
72 }
73 #[inline] fn line_col_at(&self, offset: usize) -> (u32, u32) {
74 XmlReader::line_col_at(self, offset)
75 }
76}
77use super::identity::{
78 ConstraintKind, FieldPath, NameTest, PathExpr, PathStep, SelectorPath,
79};
80use super::schema::{
81 AttributeUseKind, BlockSet, ContentModel, ElementDecl, GroupKind,
82 Particle, ProcessContents, QName, Schema, Term, TypeRef, Wildcard,
83};
84use super::types::{BuiltinType, ComplexType, DerivationMethod, SimpleType};
85
86impl Schema {
89 pub fn validate_str(&self, xml: &str) -> Result<(), ValidationError> {
90 self.validate_str_opts(xml, ValidationOptions::default())
91 }
92
93 pub fn validate_bytes(&self, xml: &[u8]) -> Result<(), ValidationError> {
94 let s = std::str::from_utf8(xml).map_err(|e| ValidationError::single(
95 ValidationIssue {
96 message: format!("invalid UTF-8: {e}"),
97 line: None, column: None, path: String::new(),
98 kind: ValidationKind::Other,
99 expected: Vec::new(), value: None, type_name: None,
100 }
101 ))?;
102 self.validate_str(s)
103 }
104
105 pub fn validate_str_opts(&self, xml: &str, opts: ValidationOptions)
106 -> Result<(), ValidationError>
107 {
108 let mut v = Validator::new(self, xml, opts);
109 v.run();
110 if v.issues.is_empty() {
111 Ok(())
112 } else {
113 Err(ValidationError { issues: v.issues })
114 }
115 }
116
117 pub fn validate_doc(&self, doc: &sup_xml_tree::dom::Document)
134 -> std::result::Result<(), ValidationError>
135 {
136 self.validate_doc_opts(doc, ValidationOptions::default())
137 }
138
139 pub fn validate_doc_opts(
142 &self, doc: &sup_xml_tree::dom::Document, opts: ValidationOptions,
143 ) -> std::result::Result<(), ValidationError> {
144 let source = DocumentEventSource::new(doc);
145 let mut v = Validator::new_with_source(self, source, opts);
146 v.run();
147 if v.issues.is_empty() {
148 Ok(())
149 } else {
150 Err(ValidationError { issues: v.issues })
151 }
152 }
153
154 pub fn validate_doc_typed(&self, doc: &sup_xml_tree::dom::Document)
167 -> (std::result::Result<(), ValidationError>, PsviTypes)
168 {
169 let source = DocumentEventSource::new(doc);
170 let mut v = Validator::new_with_source(self, source, ValidationOptions::default());
171 v.type_sink = Some(HashMap::default());
172 v.run();
173 let psvi = PsviTypes { by_node: v.type_sink.take().unwrap_or_default() };
174 let res = if v.issues.is_empty() {
175 Ok(())
176 } else {
177 Err(ValidationError { issues: v.issues })
178 };
179 (res, psvi)
180 }
181}
182
183#[derive(Default)]
193pub struct PsviTypes {
194 by_node: HashMap<usize, TypeRef>,
195}
196
197impl PsviTypes {
198 pub fn governing_type(&self, node: &sup_xml_tree::dom::Node) -> Option<&TypeRef> {
201 self.by_node.get(&(node as *const sup_xml_tree::dom::Node as usize))
202 }
203
204 pub fn is_empty(&self) -> bool { self.by_node.is_empty() }
206}
207
208const XSI_NS: &str = "http://www.w3.org/2001/XMLSchema-instance";
211
212struct Validator<'s, 'x, E: XsdEventSource<'x>> {
213 schema: &'s Schema,
214 reader: E,
215 opts: ValidationOptions,
216 _src: std::marker::PhantomData<&'x str>,
220 issues: Vec<ValidationIssue>,
221 stack: Vec<ElementCtx<'s>>,
223 attr_buf: Vec<Attr<'x>>,
224 ns_stack: Vec<HashMap<String, String>>,
227 key_scopes: Vec<KeyScope>,
231 type_sink: Option<HashMap<usize, TypeRef>>,
236}
237
238struct ElementCtx<'s> {
239 decl: Arc<ElementDecl>,
240 type_def: TypeRef,
243 cached_attrs: Vec<(QName, String)>,
248 declared_scope: usize,
251 matched_constraints: Vec<(usize, usize)>,
254 collect_depth: usize,
262 child_snapshots: Vec<ChildSnapshot>,
267 _phantom: std::marker::PhantomData<&'s ()>,
270 cursor: ContentCursor,
272 text_buf: String,
274 is_nil: bool,
276 name: QName,
285 pushed_ns: bool,
289 seen_attrs: Vec<QName>,
292 sibling_index: u32,
297 child_counters: Vec<(Arc<str>, u32)>,
311 start_offset: u32,
318}
319
320enum ContentCursor {
330 None,
331 Dfa {
333 dfa: Arc<super::dfa::Dfa>,
334 state: super::dfa::StateId,
335 },
336 Group {
339 kind: GroupKind,
340 particles: Arc<[Particle]>,
341 idx: usize,
342 cur_count: u32,
343 all_seen: HashMap<usize, u32>,
344 outer_min: u32,
349 },
350}
351
352struct KeyScope {
359 declaring_depth: usize,
362 declaring_offset: u32,
367 decl: Arc<ElementDecl>,
368 collected: Vec<Vec<KeyTuple>>,
372}
373
374type KeyTuple = Vec<Option<String>>;
377
378#[derive(Debug)]
386#[derive(Clone)]
387pub(super) struct ChildSnapshot {
388 pub(super) name: QName,
389 pub(super) attrs: Vec<(QName, String)>,
390 pub(super) text: String,
391 pub(super) children: Vec<ChildSnapshot>,
392}
393
394fn max_field_child_descent(fp: &FieldPath) -> usize {
399 fp.paths.iter()
400 .map(|p| p.steps.iter().filter(|s| matches!(s, PathStep::Child(_))).count())
401 .max()
402 .unwrap_or(0)
403}
404
405fn bump_child_counter(counters: &mut Vec<(Arc<str>, u32)>, local: &Arc<str>) -> u32 {
414 for (name, n) in counters.iter_mut() {
415 if name.as_ref() == local.as_ref() {
416 *n += 1;
417 return *n;
418 }
419 }
420 counters.push((local.clone(), 1));
421 1
422}
423
424fn snapshot_attrs<F>(attrs: &[Attr], mut to_qname: F) -> Vec<(QName, String)>
429where
430 F: FnMut(&str) -> QName,
431{
432 attrs.iter()
433 .filter(|a| {
434 let n = a.name;
435 !n.starts_with("xmlns") && !n.starts_with("xsi:")
436 })
437 .map(|a| (to_qname(a.name), a.value.to_string()))
438 .collect()
439}
440
441impl<'s, 'x> Validator<'s, 'x, XmlReader<'x>> {
442 fn new(schema: &'s Schema, xml: &'x str, opts: ValidationOptions) -> Self {
445 Self::new_with_source(schema, XmlReader::from_str(xml), opts)
446 }
447}
448
449impl<'s, 'x, E: XsdEventSource<'x>> Validator<'s, 'x, E> {
450 fn new_with_source(schema: &'s Schema, source: E, opts: ValidationOptions) -> Self {
453 Self {
454 schema,
455 reader: source,
456 opts,
457 issues: Vec::new(),
458 stack: Vec::new(),
459 attr_buf: Vec::new(),
460 ns_stack: vec![HashMap::default()],
461 key_scopes: Vec::new(),
462 type_sink: None,
463 _src: std::marker::PhantomData,
464 }
465 }
466
467 fn record_governing_type(&mut self, type_def: &TypeRef) {
471 let Some(key) = self.reader.current_node_key() else { return };
472 if let Some(sink) = self.type_sink.as_mut() {
473 sink.insert(key, type_def.clone());
474 }
475 }
476
477 fn report(&mut self, kind: ValidationKind, message: impl Into<String>) -> bool {
480 let offset = self.stack.last()
484 .map(|c| c.start_offset as usize)
485 .unwrap_or_else(|| self.reader.src_offset());
486 self.report_at(kind, message, offset)
487 }
488
489 fn report_at(&mut self, kind: ValidationKind, message: impl Into<String>, offset: usize) -> bool {
496 let (line, col) = self.reader.line_col_at(offset);
497 let path = self.current_path();
498 let issue = ValidationIssue {
499 message: message.into(),
500 line: Some(line), column: Some(col),
501 path,
502 kind,
503 expected: Vec::new(), value: None, type_name: None,
504 };
505 self.issues.push(issue);
506 if self.opts.fail_fast || self.issues.len() >= self.opts.max_issues {
507 return true; }
509 false
510 }
511
512 fn current_path(&self) -> String {
513 if self.stack.is_empty() { return String::new(); }
524 let mut s = String::new();
525 for ctx in &self.stack {
526 s.push('/');
527 s.push_str(&ctx.name.local);
528 if ctx.sibling_index >= 2 {
529 use std::fmt::Write;
530 let _ = write!(s, "[{}]", ctx.sibling_index);
531 }
532 }
533 s
534 }
535
536 fn push_ns_scope(&mut self, attrs: &[Attr<'x>]) -> bool {
548 let has_ns = attrs.iter().any(|a| {
549 let n = a.name;
550 n == "xmlns" || n.starts_with("xmlns:")
551 });
552 if !has_ns { return false; }
553
554 let mut new = self.ns_stack.last().cloned().unwrap_or_default();
555 for a in attrs {
556 let n = a.name;
557 if n == "xmlns" {
558 new.insert(String::new(), a.value.to_string());
559 } else if let Some(prefix) = n.strip_prefix("xmlns:") {
560 new.insert(prefix.to_string(), a.value.to_string());
561 }
562 }
563 self.ns_stack.push(new);
564 true
565 }
566
567 fn pop_ns_scope_if(&mut self, pushed: bool) {
568 if pushed { self.ns_stack.pop(); }
569 }
570
571 fn resolve_prefix(&self, prefix: &str) -> Option<&str> {
572 for scope in self.ns_stack.iter().rev() {
573 if let Some(uri) = scope.get(prefix) {
574 return Some(uri.as_str());
575 }
576 }
577 None
578 }
579
580 fn parse_element_qname(&self, raw: &str) -> QName {
587 match raw.split_once(':') {
588 Some((p, local)) => QName {
589 namespace: self.resolve_prefix(p).map(Arc::from),
590 local: Arc::from(local),
591 },
592 None => QName {
593 namespace: self.resolve_prefix("").map(Arc::from),
594 local: Arc::from(raw),
595 },
596 }
597 }
598
599 fn parse_attribute_qname(&self, raw: &str) -> QName {
603 match raw.split_once(':') {
604 Some((p, local)) => QName {
605 namespace: self.resolve_prefix(p).map(Arc::from),
606 local: Arc::from(local),
607 },
608 None => QName {
609 namespace: None,
610 local: Arc::from(raw),
611 },
612 }
613 }
614
615 fn parse_qname_value(&self, raw: &str) -> QName {
619 self.parse_element_qname(raw)
620 }
621
622 fn run(&mut self) {
625 loop {
626 let ev = match self.reader.next_into(&mut self.attr_buf) {
627 Ok(ev) => ev,
628 Err(e) => {
629 self.issues.push(ValidationIssue {
630 message: format!("XML parse error: {e}"),
631 line: e.line, column: e.column, path: self.current_path(),
632 kind: ValidationKind::Other,
633 expected: Vec::new(), value: None, type_name: None,
634 });
635 return;
636 }
637 };
638 match ev {
639 EventInto::Comment(_)
645 | EventInto::Pi { .. }
646 | EventInto::EntityRef { .. } => continue,
647 EventInto::StartElement { name } => {
648 let event_offset = self.reader.last_start_offset()
656 .unwrap_or_else(|| self.reader.src_offset());
657 let attrs = std::mem::take(&mut self.attr_buf);
669 let bail = self.handle_start(name, &attrs, event_offset);
670 self.attr_buf = attrs;
674 if bail { return; }
675 }
676 EventInto::EndElement { .. } => {
677 if self.handle_end() { return; }
678 }
679 EventInto::Text(t) | EventInto::CData(t) => {
680 if let Some(ctx) = self.stack.last_mut() {
681 ctx.text_buf.push_str(&t);
682 }
683 }
684 EventInto::Eof => {
685 if !self.stack.is_empty() {
686 self.report(ValidationKind::Other,
687 "unexpected EOF (unclosed elements)");
688 }
689 return;
690 }
691 }
692 }
693 }
694
695 fn handle_start(
696 &mut self,
697 name: std::borrow::Cow<'x, str>,
698 attrs: &[Attr<'x>],
699 event_offset: usize,
700 ) -> bool {
701 let pushed_ns = self.push_ns_scope(attrs);
702 let qn = self.parse_element_qname(&name);
703
704 let decl = if let Some(parent) = self.stack.last_mut() {
707 let m = match_in_cursor(&qn, &mut parent.cursor, self.schema);
709 match m {
710 MatchOutcome::Element(decl) => decl,
711 MatchOutcome::Wildcard(wc) => {
712 return self.handle_wildcard_match(&qn, attrs, wc, pushed_ns, event_offset);
714 }
715 MatchOutcome::None => {
716 let expected = expected_element_names(&parent.cursor);
723 let stop = self.report_at(ValidationKind::UnexpectedElement,
724 format!("unexpected element <{qn}>"), event_offset);
725 if let Some(last) = self.issues.last_mut() {
726 last.expected = expected;
727 }
728 if stop { return true; }
729 return self.skip_body_into_issues(pushed_ns);
731 }
732 }
733 } else {
734 match self.schema.element(&qn) {
740 Some(d) => d.clone(),
741 None => {
742 if let Some(xsi_type) = find_xsi_type(&attrs) {
743 let type_qn = self.parse_qname_value(xsi_type);
744 if let Some(tr) = self.lookup_type_by_qname(&type_qn) {
745 Arc::new(ElementDecl {
746 name: qn.clone(),
747 type_def: tr,
748 nillable: true,
749 default: None,
750 fixed: None,
751 abstract_: false,
752 substitution_group: None,
753 identity: Vec::new(),
754 block: super::schema::BlockSet::empty(),
755 final_: super::schema::BlockSet::empty(),
756 })
757 } else {
758 self.report_at(ValidationKind::UnexpectedElement,
759 format!("root element <{qn}>: xsi:type {type_qn} not declared in schema"),
760 event_offset);
761 return self.skip_body_into_issues(pushed_ns);
762 }
763 } else {
764 self.report_at(ValidationKind::UnexpectedElement,
765 format!("root element <{qn}> not declared in schema"),
766 event_offset);
767 return self.skip_body_into_issues(pushed_ns);
768 }
769 }
770 }
771 };
772
773 let mut type_def = self.resolve_type(&decl.type_def);
775
776 let mut xsi_type_override_applied = false;
789 if let Some(xsi_type) = find_xsi_type(&attrs) {
790 let override_qn = self.parse_qname_value(xsi_type);
791 if let Some(t) = self.lookup_type_by_qname(&override_qn) {
792 let declared = type_def.clone();
793 match self.derivation_methods_from(&t, &declared) {
794 None => {
795 self.report_at(ValidationKind::TypeMismatch,
796 format!("xsi:type {override_qn} does not derive from declared type"),
797 event_offset);
798 }
801 Some(methods) => {
802 let type_block = if let TypeRef::Complex(declared_ct) = &declared {
812 declared_ct.block
813 } else { BlockSet::empty() };
814 let element_blocked = (decl.block | type_block) & methods;
815 if !element_blocked.is_empty() {
816 self.report_at(ValidationKind::TypeMismatch,
817 format!(
818 "xsi:type {override_qn} blocked by block={}",
819 format_block_set(element_blocked),
820 ),
821 event_offset);
822 } else {
824 let final_blocked = if let TypeRef::Complex(declared_ct) = &declared {
830 declared_ct.final_ & methods
831 } else {
832 BlockSet::empty()
833 };
834 if !final_blocked.is_empty() {
835 self.report_at(ValidationKind::TypeMismatch,
836 format!(
837 "xsi:type {override_qn} blocked by base type final={}",
838 format_block_set(final_blocked),
839 ),
840 event_offset);
841 } else {
843 let target_is_abstract = matches!(&t,
849 TypeRef::Complex(ct) if ct.abstract_);
850 if !target_is_abstract {
851 type_def = t;
852 xsi_type_override_applied = true;
853 } else {
854 self.report_at(ValidationKind::TypeMismatch,
855 format!("xsi:type {override_qn} is abstract"),
856 event_offset);
857 }
858 }
859 }
860 }
861 }
862 } else {
863 self.report_at(ValidationKind::TypeMismatch,
864 format!("xsi:type {override_qn:?} not declared in schema"),
865 event_offset);
866 }
867 }
868
869 if decl.abstract_ && !xsi_type_override_applied {
879 self.report_at(ValidationKind::UnexpectedElement,
880 format!("element <{qn}> is abstract and cannot appear in instances"),
881 event_offset);
882 return self.skip_body_into_issues(pushed_ns);
883 }
884
885 let is_nil = match find_xsi_nil(&attrs) {
892 None => false,
893 Some(raw) => match parse_xsi_nil(raw) {
894 XsiNilParse::True => true,
895 XsiNilParse::False => false,
896 XsiNilParse::Invalid(bad) => {
897 self.report_at(ValidationKind::TypeMismatch,
898 format!("xsi:nil value {bad:?} is not a valid xs:boolean \
899 (expected \"true\", \"false\", \"1\", or \"0\")"),
900 event_offset);
901 false
902 }
903 },
904 };
905 if is_nil && !decl.nillable {
906 self.report_at(ValidationKind::NillableViolation,
908 format!("xsi:nil on non-nillable element <{qn}>"),
909 event_offset);
910 }
911
912 let seen_attrs = self.validate_attrs_against_type(&type_def, &attrs, event_offset);
917
918 let cursor = if is_nil { ContentCursor::None } else { build_cursor(&type_def) };
922
923 let declared_scope = if !decl.identity.is_empty() {
932 let collected = vec![Vec::new(); decl.identity.len()];
933 self.key_scopes.push(KeyScope {
934 declaring_depth: self.stack.len(),
935 declaring_offset: event_offset as u32,
936 decl: decl.clone(),
937 collected,
938 });
939 self.key_scopes.len() - 1
940 } else {
941 usize::MAX
942 };
943
944 let parent_collect_depth = self.stack.last()
949 .map(|p| p.collect_depth).unwrap_or(0);
950 let parent_collects = parent_collect_depth > 0;
951 let has_assertions = matches!(
957 &type_def, TypeRef::Complex(ct) if !ct.assertions.is_empty()
958 );
959 let need_capture = parent_collects || has_assertions;
960 let (matched, mut cached_attrs) = self.check_active_scopes(&qn, &attrs);
961 if cached_attrs.is_empty() && need_capture {
962 cached_attrs = snapshot_attrs(&attrs, |s| self.parse_attribute_qname(s));
963 }
964 let own_collect_depth = if has_assertions {
969 usize::MAX
970 } else {
971 matched.iter().map(|(si, ci)| {
972 self.key_scopes[*si].decl.identity[*ci].fields.iter()
973 .map(max_field_child_descent)
974 .max().unwrap_or(0)
975 }).max().unwrap_or(0)
976 };
977 let collect_depth = own_collect_depth
981 .max(parent_collect_depth.saturating_sub(1));
982
983 let sibling_index = match self.stack.last_mut() {
986 Some(parent) => bump_child_counter(&mut parent.child_counters, &qn.local),
987 None => 1,
988 };
989
990 self.record_governing_type(&type_def);
991 self.stack.push(ElementCtx {
992 decl,
993 type_def,
994 cursor,
995 text_buf: String::new(),
996 is_nil,
997 name: qn,
998 pushed_ns,
999 seen_attrs,
1000 cached_attrs,
1001 declared_scope,
1002 matched_constraints: matched,
1003 collect_depth,
1004 child_snapshots: Vec::new(),
1005 start_offset: event_offset as u32,
1006 _phantom: std::marker::PhantomData,
1007 sibling_index,
1008 child_counters: Vec::new(),
1009 });
1010 false
1011 }
1012
1013 fn check_active_scopes(
1019 &self,
1020 qn: &QName,
1021 attrs: &[Attr<'x>],
1022 ) -> (Vec<(usize, usize)>, Vec<(QName, String)>) {
1023 if self.key_scopes.is_empty() {
1024 return (Vec::new(), Vec::new());
1025 }
1026 let depth = self.stack.len();
1027 let mut matched = Vec::new();
1028 for (si, scope) in self.key_scopes.iter().enumerate() {
1029 let rel = depth.saturating_sub(scope.declaring_depth);
1030 for (ci, c) in scope.decl.identity.iter().enumerate() {
1033 if selector_matches(&c.selector, &self.stack, qn, rel) {
1034 matched.push((si, ci));
1035 }
1036 }
1037 }
1038 let cached_attrs = if matched.is_empty() {
1039 Vec::new()
1040 } else {
1041 snapshot_attrs(attrs, |s| self.parse_attribute_qname(s))
1042 };
1043 (matched, cached_attrs)
1044 }
1045
1046 fn run_simple_type_assertions(
1050 &mut self,
1051 st: &super::types::SimpleType,
1052 value: &str,
1053 ctx_off: usize,
1054 ) {
1055 if st.assertions.is_empty() { return; }
1056 for a in &st.assertions {
1057 use super::assertion::{eval_simple_assertion, AssertOutcome};
1058 match eval_simple_assertion(a, value) {
1059 AssertOutcome::Pass => {}
1060 AssertOutcome::Fail => {
1061 self.report_at(ValidationKind::AssertionViolation,
1062 format!("assertion failed: {}", a.test), ctx_off);
1063 }
1064 AssertOutcome::Unevaluable(_) => {}
1065 }
1066 }
1067 }
1068
1069 fn handle_end(&mut self) -> bool {
1070 let mut ctx = self.stack.pop().expect("EndElement with empty stack");
1071 self.pop_ns_scope_if(ctx.pushed_ns);
1072
1073 let element_simple_type =
1083 self.field_dot_simple_type(&ctx.type_def);
1084 for (si, ci) in &ctx.matched_constraints {
1085 let constraint = self.key_scopes[*si].decl.identity[*ci].clone();
1086 let fields = constraint.fields.clone();
1087 let canonicalisable =
1097 fields.iter().all(field_path_is_dot)
1098 && self.constraint_peers_all_dot(*si, *ci);
1099 let mut tuple: KeyTuple = Vec::with_capacity(fields.len());
1100 let mut ambiguous = false;
1101 for fp in fields {
1102 match eval_field(
1103 &fp, &ctx.cached_attrs, &ctx.text_buf, &ctx.child_snapshots,
1104 ) {
1105 FieldEval::Single(v) => {
1106 let canon = if canonicalisable {
1107 canonical_field_key(&v, element_simple_type.as_ref())
1108 } else {
1109 v
1110 };
1111 tuple.push(Some(canon));
1112 }
1113 FieldEval::Missing => tuple.push(None),
1114 FieldEval::Ambiguous => { ambiguous = true; tuple.push(None); }
1115 }
1116 }
1117 if ambiguous {
1118 self.report(ValidationKind::Other, format!(
1122 "<xs:{} {:?}>: a field xpath selected more than one node",
1123 match constraint.kind {
1124 ConstraintKind::Key => "key",
1125 ConstraintKind::Unique => "unique",
1126 ConstraintKind::KeyRef => "keyref",
1127 },
1128 constraint.name.local,
1129 ));
1130 }
1131 self.key_scopes[*si].collected[*ci].push(tuple);
1132 }
1133 if ctx.declared_scope != usize::MAX {
1136 let scope = self.key_scopes.pop().expect("scope stack out of sync");
1137 self.finalize_key_scope(&scope);
1138 }
1139 if let Some(parent) = self.stack.last_mut() {
1143 if parent.collect_depth > 0 {
1144 parent.child_snapshots.push(ChildSnapshot {
1145 name: ctx.name.clone(),
1146 attrs: ctx.cached_attrs.clone(),
1147 text: ctx.text_buf.clone(),
1148 children: std::mem::take(&mut ctx.child_snapshots),
1149 });
1150 }
1151 }
1152
1153 let ctx_off = ctx.start_offset as usize;
1158
1159 if let ContentCursor::Dfa { dfa, state } = &ctx.cursor {
1161 if !dfa.is_accept(*state) {
1162 let expected: Vec<String> = dfa.states[*state as usize]
1166 .on_element.iter()
1167 .map(|t| t.name.local.to_string())
1168 .collect();
1169 let msg = if expected.is_empty() {
1170 "element content does not match the schema (no valid continuation)".to_string()
1171 } else if expected.len() == 1 {
1172 format!("missing required element <{}>", expected[0])
1173 } else {
1174 format!("missing required element (one of: {})", expected.join(", "))
1175 };
1176 self.report_at(ValidationKind::MissingRequiredElement, msg, ctx_off);
1177 }
1178 }
1179 if let ContentCursor::Group { kind, particles, idx, cur_count, all_seen, outer_min, .. } = &ctx.cursor {
1180 match kind {
1181 GroupKind::Sequence => {
1182 if *idx < particles.len() {
1184 let p = &particles[*idx];
1186 if *cur_count < p.min_occurs {
1187 let _ = self.handle_min_occurs_violation(p, ctx_off);
1188 }
1189 for p in &particles[*idx + 1..] {
1191 if p.min_occurs > 0 {
1192 let _ = self.handle_min_occurs_violation(p, ctx_off);
1193 }
1194 }
1195 }
1196 }
1197 GroupKind::Choice => {
1198 if *cur_count == 0 && !particles.is_empty() {
1201 self.report_at(ValidationKind::MissingRequiredElement,
1202 "no branch of <xs:choice> matched", ctx_off);
1203 }
1204 }
1205 GroupKind::All => {
1206 let saw_anything = all_seen.values().any(|&n| n > 0);
1210 if *outer_min == 0 && !saw_anything {
1211 } else {
1213 for (i, p) in particles.iter().enumerate() {
1214 let seen = all_seen.get(&i).copied().unwrap_or(0);
1215 if seen < p.min_occurs {
1216 let _ = self.handle_min_occurs_violation(p, ctx_off);
1217 }
1218 }
1219 }
1220 }
1221 }
1222 }
1223
1224 match (&ctx.type_def, ctx.is_nil) {
1226 (_, true) => {
1227 if !ctx.text_buf.trim().is_empty() {
1229 self.report_at(ValidationKind::NillableViolation,
1230 "xsi:nil element must have empty content", ctx_off);
1231 }
1232 }
1233 (TypeRef::Simple(st), _) => {
1234 let real = self.resolve_simple_type(st);
1235 let to_check = effective_text(&ctx.text_buf, ctx.decl.default.as_deref(),
1240 ctx.decl.fixed.as_deref());
1241 if let Err(e) = real.validate_only(to_check) {
1242 let elem_local = ctx.decl.name.local.to_string();
1243 self.report_at(e.kind, format!("element content: {}", e.message), ctx_off);
1244 if let Some(last) = self.issues.last_mut() {
1245 last.value = Some(to_check.to_string());
1246 last.type_name = Some(format!("xs:{}", real.builtin.name()));
1247 let idx = if ctx.sibling_index >= 2 {
1253 format!("[{}]", ctx.sibling_index)
1254 } else {
1255 String::new()
1256 };
1257 last.path = format!("{}/{elem_local}{idx}", last.path);
1258 }
1259 }
1260 self.run_simple_type_assertions(&real, to_check, ctx_off);
1264 }
1265 (TypeRef::Complex(ct), _) => {
1266 match &ct.content {
1267 ContentModel::Empty => {
1268 if !ctx.text_buf.trim().is_empty() {
1269 self.report_at(ValidationKind::TypeMismatch,
1270 "complexType with empty content cannot have text", ctx_off);
1271 }
1272 }
1273 ContentModel::Simple(st) => {
1274 let effective = self.simple_content_effective_type(ct, st);
1283 let real = self.resolve_simple_type(&effective);
1284 let to_check = effective_text(&ctx.text_buf, ctx.decl.default.as_deref(),
1285 ctx.decl.fixed.as_deref());
1286 if let Err(e) = real.validate_only(to_check) {
1287 let elem_local = ctx.decl.name.local.to_string();
1288 self.report_at(e.kind,
1289 format!("element content: {}", e.message), ctx_off);
1290 if let Some(last) = self.issues.last_mut() {
1291 last.value = Some(to_check.to_string());
1292 last.type_name = Some(format!("xs:{}", real.builtin.name()));
1293 let idx = if ctx.sibling_index >= 2 {
1299 format!("[{}]", ctx.sibling_index)
1300 } else {
1301 String::new()
1302 };
1303 last.path = format!("{}/{elem_local}{idx}", last.path);
1304 }
1305 }
1306 self.run_simple_type_assertions(&real, to_check, ctx_off);
1307 }
1308 ContentModel::Complex { mixed, .. } => {
1309 if !*mixed && !ctx.text_buf.trim().is_empty() {
1310 self.report_at(ValidationKind::TypeMismatch,
1311 "non-mixed complexType cannot have text content", ctx_off);
1312 }
1313 }
1314 }
1315 }
1316 }
1317
1318 if !ctx.is_nil
1329 && let Some(fixed) = &ctx.decl.fixed
1330 && !ctx.text_buf.is_empty()
1333 && ctx.text_buf.trim() != fixed.trim()
1334 {
1335 self.report_at(ValidationKind::TypeMismatch,
1336 format!("element content {:?} doesn't match fixed value {:?}",
1337 ctx.text_buf, fixed), ctx_off);
1338 }
1339
1340 let _ = ctx.seen_attrs;
1343
1344 if let TypeRef::Complex(ct) = &ctx.type_def {
1350 if !ct.assertions.is_empty() {
1351 let snapshot = ChildSnapshot {
1352 name: ctx.name.clone(),
1353 attrs: ctx.cached_attrs.clone(),
1354 text: ctx.text_buf.clone(),
1355 children: ctx.child_snapshots.clone(),
1356 };
1357 for a in &ct.assertions {
1358 use super::assertion::{eval_complex_assert, AssertOutcome};
1359 match eval_complex_assert(a, &snapshot) {
1360 AssertOutcome::Pass => {}
1361 AssertOutcome::Fail => {
1362 self.report_at(ValidationKind::AssertionViolation,
1363 format!("assertion failed: {}", a.test), ctx_off);
1364 }
1365 AssertOutcome::Unevaluable(_why) => {
1366 }
1371 }
1372 }
1373 }
1374 }
1375
1376 false
1377 }
1378
1379 fn handle_wildcard_match(
1380 &mut self,
1381 qn: &QName,
1382 attrs: &[Attr<'x>],
1383 wc: Wildcard,
1384 pushed_ns: bool,
1385 event_offset: usize,
1386 ) -> bool {
1387 match wc.process_contents {
1388 ProcessContents::Skip => {
1389 self.skip_body_into_issues(pushed_ns)
1390 }
1391 ProcessContents::Lax => {
1392 if let Some(decl) = self.schema.element(qn) {
1393 let decl = decl.clone();
1394 self.push_decl_ctx(qn, decl, attrs, pushed_ns, event_offset);
1395 false
1396 } else {
1397 self.skip_body_into_issues(pushed_ns)
1398 }
1399 }
1400 ProcessContents::Strict => {
1401 if let Some(decl) = self.schema.element(qn) {
1402 let decl = decl.clone();
1403 self.push_decl_ctx(qn, decl, attrs, pushed_ns, event_offset);
1404 false
1405 } else {
1406 self.report_at(ValidationKind::UnexpectedElement,
1407 format!("strict wildcard: <{qn}> not declared in schema"),
1408 event_offset);
1409 self.skip_body_into_issues(pushed_ns)
1410 }
1411 }
1412 }
1413 }
1414
1415 fn validate_attrs_against_type(
1421 &mut self,
1422 type_def: &TypeRef,
1423 attrs: &[Attr<'x>],
1424 event_offset: usize,
1425 ) -> Vec<QName> {
1426 let mut seen_attrs = Vec::with_capacity(attrs.len());
1427 let TypeRef::Complex(ct) = type_def else { return seen_attrs };
1428 for a in attrs {
1429 let aname = a.name;
1430 if aname == "xmlns" || aname.starts_with("xmlns:") { continue; }
1431 let (prefix_opt, local): (Option<&str>, &str) =
1435 match aname.split_once(':') {
1436 Some((p, l)) => (Some(p), l),
1437 None => (None, aname),
1438 };
1439 let ns_uri: Option<&str> = prefix_opt.and_then(|p| self.resolve_prefix(p));
1440 if ns_uri == Some(XSI_NS) { continue; }
1441 let matched = ct.attributes.iter().find(|au| {
1457 au.use_kind != AttributeUseKind::Prohibited
1458 && au.decl.name.local.as_ref() == local
1459 && au.decl.name.namespace.as_deref() == ns_uri
1460 });
1461 match matched {
1462 Some(au) => {
1463 let referenced = self.schema.attribute(&au.decl.name);
1470 let resolved_type = referenced
1471 .map(|d| d.type_def.clone())
1472 .unwrap_or_else(|| au.decl.type_def.clone());
1473 let st = self.resolve_simple_type(&resolved_type);
1474 let val = a.value.as_ref();
1475 if let Err(e) = st.validate_only(val) {
1476 self.report_at(e.kind,
1477 format!("attribute {}: {}", au.decl.name, e.message),
1478 event_offset);
1479 }
1480 let fixed = au.fixed.as_deref()
1481 .or(au.decl.fixed.as_deref())
1482 .or_else(|| referenced.and_then(|d| d.fixed.as_deref()));
1483 if let Some(f) = fixed {
1484 if val.trim() != f.trim() {
1485 self.report_at(ValidationKind::TypeMismatch,
1486 format!("attribute {} value {val:?} \
1487 doesn't match fixed value {f:?}",
1488 au.decl.name),
1489 event_offset);
1490 }
1491 }
1492 seen_attrs.push(au.decl.name.clone());
1498 }
1499 None => {
1500 let attr_qn = QName {
1504 namespace: ns_uri.map(Arc::from),
1505 local: Arc::from(local),
1506 };
1507 if let Some(wc) = &ct.any_attribute {
1508 if attr_wildcard_accepts_qname(wc, &attr_qn, self.schema, ct) {
1509 seen_attrs.push(attr_qn);
1522 continue;
1523 }
1524 }
1525 self.report_at(ValidationKind::UnexpectedAttribute,
1526 format!("unexpected attribute {attr_qn}"),
1527 event_offset);
1528 }
1529 }
1530 }
1531 for au in &ct.attributes {
1532 let fill_value: Option<&str> =
1539 if self.opts.apply_attribute_defaults && au.decl.name.namespace.is_none() {
1540 au.default.as_deref()
1541 .or(au.decl.default.as_deref())
1542 .or(au.fixed.as_deref())
1543 .or(au.decl.fixed.as_deref())
1544 } else {
1545 None
1546 };
1547 if au.use_kind != AttributeUseKind::Required && fill_value.is_none() {
1548 continue;
1549 }
1550 let present = seen_attrs.iter().any(|n|
1551 n == &au.decl.name
1552 || (n.local == au.decl.name.local
1553 && n.namespace.is_none()
1554 && au.decl.name.namespace.is_none()));
1555 if present { continue; }
1556 if let Some(value) = fill_value {
1561 self.reader.fill_default_attr(au.decl.name.local.as_ref(), value);
1562 continue;
1563 }
1564 if au.use_kind == AttributeUseKind::Required {
1565 self.report_at(ValidationKind::MissingRequiredAttribute,
1566 format!("missing required attribute {}", au.decl.name),
1567 event_offset);
1568 }
1569 }
1570 seen_attrs
1571 }
1572
1573 fn push_decl_ctx(&mut self, qn: &QName, decl: Arc<ElementDecl>, attrs: &[Attr<'x>],
1574 pushed_ns: bool, event_offset: usize,
1575 ) {
1576 let type_def = self.resolve_type(&decl.type_def);
1577 let _ = self.validate_attrs_against_type(&type_def, &attrs, event_offset);
1583 let cursor = build_cursor(&type_def);
1584 let declared_scope = if !decl.identity.is_empty() {
1585 let collected = vec![Vec::new(); decl.identity.len()];
1586 self.key_scopes.push(KeyScope {
1587 declaring_depth: self.stack.len(),
1588 declaring_offset: event_offset as u32,
1589 decl: decl.clone(),
1590 collected,
1591 });
1592 self.key_scopes.len() - 1
1593 } else { usize::MAX };
1594 let parent_collect_depth = self.stack.last()
1595 .map(|p| p.collect_depth).unwrap_or(0);
1596 let parent_collects = parent_collect_depth > 0;
1597 let (matched, mut cached_attrs) = self.check_active_scopes(qn, &attrs);
1598 if cached_attrs.is_empty() && parent_collects {
1599 cached_attrs = snapshot_attrs(&attrs, |s| self.parse_attribute_qname(s));
1600 }
1601 let own_collect_depth = matched.iter().map(|(si, ci)| {
1602 self.key_scopes[*si].decl.identity[*ci].fields.iter()
1603 .map(max_field_child_descent)
1604 .max().unwrap_or(0)
1605 }).max().unwrap_or(0);
1606 let collect_depth = own_collect_depth
1607 .max(parent_collect_depth.saturating_sub(1));
1608 let sibling_index = match self.stack.last_mut() {
1609 Some(parent) => bump_child_counter(&mut parent.child_counters, &qn.local),
1610 None => 1,
1611 };
1612 self.record_governing_type(&type_def);
1613 self.stack.push(ElementCtx {
1614 decl,
1615 type_def,
1616 cursor,
1617 text_buf: String::new(),
1618 is_nil: false,
1619 name: qn.clone(),
1620 pushed_ns,
1621 seen_attrs: Vec::new(),
1622 cached_attrs,
1623 declared_scope,
1624 matched_constraints: matched,
1625 collect_depth,
1626 child_snapshots: Vec::new(),
1627 start_offset: event_offset as u32,
1628 _phantom: std::marker::PhantomData,
1629 sibling_index,
1630 child_counters: Vec::new(),
1631 });
1632 let _ = attrs;
1635 }
1636
1637 fn skip_body_into_issues(&mut self, pushed_ns: bool) -> bool {
1643 let mut depth = 1usize;
1644 while depth > 0 {
1645 match self.reader.next_into(&mut self.attr_buf) {
1646 Ok(EventInto::StartElement { .. }) => depth += 1,
1647 Ok(EventInto::EndElement { .. }) => depth -= 1,
1648 Ok(EventInto::Eof) => {
1649 self.report(ValidationKind::Other, "unexpected EOF in skipped subtree");
1650 return true;
1651 }
1652 Err(e) => {
1653 self.issues.push(ValidationIssue {
1654 message: format!("XML parse error: {e}"),
1655 line: e.line, column: e.column, path: self.current_path(),
1656 kind: ValidationKind::Other,
1657 expected: Vec::new(), value: None, type_name: None,
1658 });
1659 return true;
1660 }
1661 _ => {}
1662 }
1663 }
1664 self.pop_ns_scope_if(pushed_ns);
1665 false
1666 }
1667
1668 fn handle_min_occurs_violation(&mut self, p: &Particle, at_offset: usize) -> bool {
1669 let what = match &p.term {
1670 Term::Element(e) => format!("element <{}>", e.name),
1671 Term::Group { .. } => "group".to_string(),
1672 Term::Wildcard(_) => "wildcard".to_string(),
1673 Term::GroupRef(name) => format!("group {name}"),
1674 };
1675 self.report_at(ValidationKind::MissingRequiredElement,
1676 format!("missing required {what} (minOccurs={})", p.min_occurs),
1677 at_offset)
1678 }
1679
1680 fn resolve_type(&self, t: &TypeRef) -> TypeRef {
1686 if let TypeRef::Simple(st) = t {
1687 if let Some(name) = &st.name {
1688 if let Some(rest) = name.strip_prefix("UNRESOLVED:") {
1689 let qn = parse_unresolved_marker(rest);
1690 if let Some(real) = self.lookup_type_by_qname(&qn) {
1691 return real;
1692 }
1693 }
1694 }
1695 }
1696 t.clone()
1697 }
1698
1699 fn constraint_peers_all_dot(&self, scope_idx: usize, constraint_idx: usize) -> bool {
1709 use super::identity::ConstraintKind;
1710 let me = &self.key_scopes[scope_idx].decl.identity[constraint_idx];
1711 match me.kind {
1712 ConstraintKind::Key | ConstraintKind::Unique => {
1713 for scope in &self.key_scopes {
1714 for c in scope.decl.identity.iter() {
1715 if c.kind != ConstraintKind::KeyRef { continue; }
1716 if c.refer.as_ref() != Some(&me.name) { continue; }
1717 if !c.fields.iter().all(field_path_is_dot) {
1718 return false;
1719 }
1720 }
1721 }
1722 true
1723 }
1724 ConstraintKind::KeyRef => {
1725 let Some(target_name) = me.refer.as_ref() else { return true; };
1726 for scope in &self.key_scopes {
1727 for c in scope.decl.identity.iter() {
1728 if !matches!(c.kind,
1729 ConstraintKind::Key | ConstraintKind::Unique) { continue; }
1730 if &c.name != target_name { continue; }
1731 if !c.fields.iter().all(field_path_is_dot) {
1732 return false;
1733 }
1734 }
1735 }
1736 true
1737 }
1738 }
1739 }
1740
1741 fn field_dot_simple_type(
1747 &self, type_def: &TypeRef,
1748 ) -> Option<Arc<SimpleType>> {
1749 match type_def {
1750 TypeRef::Simple(st) => Some(self.resolve_simple_type(st)),
1751 TypeRef::Complex(ct) => match &ct.content {
1752 ContentModel::Simple(st) => {
1753 let effective = self.simple_content_effective_type(ct, st);
1754 Some(self.resolve_simple_type(&effective))
1755 }
1756 _ => None,
1757 },
1758 }
1759 }
1760
1761 fn simple_content_effective_type(
1774 &self, ct: &Arc<ComplexType>, current: &Arc<SimpleType>,
1775 ) -> Arc<SimpleType> {
1776 let is_placeholder = current.name.is_none()
1783 && matches!(current.builtin, super::types::BuiltinType::String);
1784 if !is_placeholder { return current.clone(); }
1785 let Some(d) = &ct.derivation else { return current.clone(); };
1786 if d.method != super::types::DerivationMethod::Extension {
1787 return current.clone();
1788 }
1789 let base = self.resolve_type(&d.base);
1790 match base {
1791 TypeRef::Simple(s) => s,
1792 TypeRef::Complex(_) => current.clone(),
1793 }
1794 }
1795
1796 fn resolve_simple_type(&self, st: &Arc<SimpleType>) -> Arc<SimpleType> {
1797 let top = if let Some(name) = &st.name {
1799 if let Some(rest) = name.strip_prefix("UNRESOLVED:") {
1800 let qn = parse_unresolved_marker(rest);
1801 if let Some(TypeRef::Simple(real)) = self.lookup_type_by_qname(&qn) {
1802 real
1803 } else {
1804 st.clone()
1805 }
1806 } else { st.clone() }
1807 } else { st.clone() };
1808
1809 match &top.variety {
1815 super::types::Variety::Atomic => top,
1816 super::types::Variety::List { item_type } => {
1817 let new_item = self.resolve_simple_type(item_type);
1818 if Arc::ptr_eq(&new_item, item_type) {
1819 top
1820 } else {
1821 let mut t: SimpleType = (*top).clone();
1822 t.variety = super::types::Variety::List { item_type: new_item };
1823 Arc::new(t)
1824 }
1825 }
1826 super::types::Variety::Union { members } => {
1827 let new_members: Vec<Arc<SimpleType>> = members.iter()
1828 .map(|m| self.resolve_simple_type(m))
1829 .collect();
1830 let changed = new_members.iter().zip(members.iter())
1831 .any(|(a, b)| !Arc::ptr_eq(a, b));
1832 if !changed {
1833 top
1834 } else {
1835 let mut t: SimpleType = (*top).clone();
1836 t.variety = super::types::Variety::Union { members: new_members };
1837 Arc::new(t)
1838 }
1839 }
1840 }
1841 }
1842
1843 fn lookup_type_by_qname(&self, qn: &QName) -> Option<TypeRef> {
1844 if qn.namespace.as_deref() == Some(QName::XSD_NS) {
1846 if let Some(b) = BuiltinType::from_name(&qn.local) {
1847 return Some(TypeRef::Simple(Arc::new(SimpleType {
1848 name: Some(qn.local.clone()),
1849 builtin: b,
1850 facets: super::facets::FacetSet::default(),
1851 whitespace: b.default_whitespace(),
1852 variety: super::types::Variety::Atomic,
1853 final_: super::schema::BlockSet::default(),
1854 assertions: Vec::new(),
1855 })));
1856 }
1857 }
1858 self.schema.type_def(qn).cloned()
1860 }
1861}
1862
1863fn build_cursor(type_def: &TypeRef) -> ContentCursor {
1864 match type_def {
1865 TypeRef::Simple(_) => ContentCursor::None,
1866 TypeRef::Complex(ct) => match ct.matcher.get() {
1867 Some(super::dfa::ContentMatcher::Dfa(dfa)) => ContentCursor::Dfa {
1868 dfa: dfa.clone(),
1869 state: dfa.initial,
1870 },
1871 Some(super::dfa::ContentMatcher::All) => match &ct.content {
1872 ContentModel::Complex { root: Particle { term: Term::Group { kind, particles }, min_occurs, .. }, .. } => {
1873 ContentCursor::Group {
1874 kind: *kind,
1875 particles: Arc::clone(particles),
1876 idx: 0,
1877 cur_count: 0,
1878 all_seen: HashMap::default(),
1879 outer_min: *min_occurs,
1880 }
1881 }
1882 _ => ContentCursor::None,
1883 },
1884 Some(super::dfa::ContentMatcher::None) | None => ContentCursor::None,
1885 },
1886 }
1887}
1888
1889fn find_xsi_type<'a>(attrs: &'a [Attr]) -> Option<&'a str> {
1898 attrs.iter().find(|a| a.name() == "xsi:type").map(|a| a.value.as_ref())
1899}
1900
1901fn effective_text<'a>(text: &'a str, default: Option<&'a str>, fixed: Option<&'a str>) -> &'a str {
1908 if !text.is_empty() { return text; }
1909 fixed.or(default).unwrap_or(text)
1910}
1911
1912fn find_xsi_nil<'a>(attrs: &'a [Attr]) -> Option<&'a str> {
1913 attrs.iter().find(|a| a.name() == "xsi:nil").map(|a| a.value.as_ref())
1914}
1915
1916enum XsiNilParse<'a> {
1923 True,
1924 False,
1925 Invalid(&'a str),
1926}
1927
1928fn parse_xsi_nil(raw: &str) -> XsiNilParse<'_> {
1929 match raw {
1930 "true" | "1" => XsiNilParse::True,
1931 "false" | "0" => XsiNilParse::False,
1932 other => XsiNilParse::Invalid(other),
1933 }
1934}
1935
1936fn parse_unresolved_marker(s: &str) -> QName {
1937 if let Some(rest) = s.strip_prefix('{') {
1939 if let Some(end) = rest.find('}') {
1940 let ns = &rest[..end];
1941 let local = &rest[end + 1..];
1942 return QName::new(if ns.is_empty() { None } else { Some(ns) }, local);
1943 }
1944 }
1945 QName::new(None, s)
1946}
1947
1948enum MatchOutcome {
1951 Element(Arc<ElementDecl>),
1952 Wildcard(Wildcard),
1953 None,
1954}
1955
1956fn expected_element_names(cursor: &ContentCursor) -> Vec<String> {
1961 match cursor {
1962 ContentCursor::Dfa { dfa, state } => dfa.states[*state as usize]
1963 .on_element
1964 .iter()
1965 .map(|t| t.name.local.to_string())
1966 .collect(),
1967 ContentCursor::Group { particles, idx, .. } => particles
1968 .get(*idx)
1969 .map(particle_element_names)
1970 .unwrap_or_default(),
1971 ContentCursor::None => Vec::new(),
1972 }
1973}
1974
1975fn particle_element_names(p: &Particle) -> Vec<String> {
1976 match &p.term {
1977 super::schema::Term::Element(decl) => vec![decl.name.local.to_string()],
1978 super::schema::Term::Group { particles, .. } => {
1979 particles.iter().flat_map(particle_element_names).collect()
1980 }
1981 _ => Vec::new(),
1982 }
1983}
1984
1985fn match_in_cursor(qn: &QName, cursor: &mut ContentCursor, schema: &Schema) -> MatchOutcome {
1986 match cursor {
1987 ContentCursor::None => MatchOutcome::None,
1988 ContentCursor::Dfa { dfa, state } => {
1989 let target_ns = schema.target_namespace();
1990 let siblings = dfa.defined_siblings.clone();
1991 let step = dfa.step(*state, qn, |wc, qn| {
1992 super::dfa::wildcard_admits(
1993 wc, qn, target_ns,
1994 |q| schema.element(q).is_some(),
1995 |q| siblings.iter().any(|s| s == q),
1996 )
1997 });
1998 match step {
1999 Some(super::dfa::DfaTransition::Element { next, decl }) => {
2000 *state = next;
2001 MatchOutcome::Element(decl)
2002 }
2003 Some(super::dfa::DfaTransition::Wildcard { next, process_contents }) => {
2004 *state = next;
2005 MatchOutcome::Wildcard(super::schema::Wildcard {
2009 namespaces: super::schema::NamespaceConstraint::Any,
2010 process_contents,
2011 not_qnames: Vec::new(),
2012 not_namespaces: Vec::new(),
2013 not_qname_defined: false,
2014 not_qname_defined_sibling: false,
2015 })
2016 }
2017 None => MatchOutcome::None,
2018 }
2019 }
2020 ContentCursor::Group { kind, particles, idx, cur_count, all_seen, .. } => {
2021 match kind {
2022 GroupKind::Sequence => match_sequence(qn, particles, idx, cur_count, schema),
2023 GroupKind::Choice => match_choice(qn, particles, idx, cur_count, schema),
2024 GroupKind::All => match_all(qn, particles, all_seen, schema),
2025 }
2026 }
2027 }
2028}
2029
2030fn match_sequence(
2031 qn: &QName,
2032 particles: &Arc<[Particle]>,
2033 idx: &mut usize,
2034 cur_count: &mut u32,
2035 schema: &Schema,
2036) -> MatchOutcome {
2037 while *idx < particles.len() {
2038 let p = &particles[*idx];
2039 if particle_accepts(p, qn, schema) {
2040 if !p.max_occurs.allows(*cur_count + 1) {
2042 *idx += 1;
2043 *cur_count = 0;
2044 continue;
2045 }
2046 *cur_count += 1;
2047 return outcome_for(p, qn, schema);
2048 }
2049 if *cur_count < p.min_occurs {
2051 return MatchOutcome::None;
2052 }
2053 *idx += 1;
2054 *cur_count = 0;
2055 }
2056 MatchOutcome::None
2057}
2058
2059fn match_choice(
2060 qn: &QName,
2061 particles: &Arc<[Particle]>,
2062 idx: &mut usize,
2063 cur_count: &mut u32,
2064 schema: &Schema,
2065) -> MatchOutcome {
2066 if *cur_count == 0 {
2068 for (i, p) in particles.iter().enumerate() {
2069 if particle_accepts(p, qn, schema) {
2070 *idx = i;
2071 *cur_count = 1;
2072 return outcome_for(p, qn, schema);
2073 }
2074 }
2075 return MatchOutcome::None;
2076 }
2077 let p = &particles[*idx];
2079 if particle_accepts(p, qn, schema) && p.max_occurs.allows(*cur_count + 1) {
2080 *cur_count += 1;
2081 return outcome_for(p, qn, schema);
2082 }
2083 MatchOutcome::None
2084}
2085
2086fn match_all(
2087 qn: &QName,
2088 particles: &Arc<[Particle]>,
2089 all_seen: &mut HashMap<usize, u32>,
2090 schema: &Schema,
2091) -> MatchOutcome {
2092 let siblings = collect_particle_siblings(particles);
2093 for (i, p) in particles.iter().enumerate() {
2094 if particle_accepts_with_siblings(p, qn, schema, &siblings) {
2095 let seen = all_seen.entry(i).or_insert(0);
2096 if !p.max_occurs.allows(*seen + 1) {
2097 continue;
2098 }
2099 *seen += 1;
2100 return outcome_for(p, qn, schema);
2101 }
2102 }
2103 MatchOutcome::None
2104}
2105
2106fn collect_particle_siblings(particles: &[Particle]) -> Vec<QName> {
2112 fn walk(p: &Particle, out: &mut Vec<QName>) {
2113 match &p.term {
2114 Term::Element(decl) => {
2115 if !out.iter().any(|n| n == &decl.name) {
2116 out.push(decl.name.clone());
2117 }
2118 }
2119 Term::Group { particles, .. } => {
2120 for c in particles.iter() { walk(c, out); }
2121 }
2122 Term::Wildcard(_) | Term::GroupRef(_) => {}
2123 }
2124 }
2125 let mut out = Vec::new();
2126 for p in particles { walk(p, &mut out); }
2127 out
2128}
2129
2130fn particle_accepts(p: &Particle, qn: &QName, schema: &Schema) -> bool {
2131 match &p.term {
2132 Term::Element(decl) => {
2133 if &decl.name == qn { return true; }
2134 if decl.block.contains(super::schema::BlockSet::SUBSTITUTION) {
2137 return false;
2138 }
2139 for sub in schema.substitutes_for(&decl.name) {
2140 if &sub.name == qn { return true; }
2141 }
2142 false
2143 }
2144 Term::Wildcard(wc) => wildcard_accepts_qname(wc, qn, schema, &[]),
2145 Term::Group { particles, .. } => particles.iter().any(|p2| particle_accepts(p2, qn, schema)),
2146 Term::GroupRef(_) => false,
2148 }
2149}
2150
2151fn particle_accepts_with_siblings(
2158 p: &Particle,
2159 qn: &QName,
2160 schema: &Schema,
2161 siblings: &[QName],
2162) -> bool {
2163 match &p.term {
2164 Term::Wildcard(wc) => wildcard_accepts_qname(wc, qn, schema, siblings),
2165 Term::Group { particles, .. } => particles
2166 .iter()
2167 .any(|p2| particle_accepts_with_siblings(p2, qn, schema, siblings)),
2168 _ => particle_accepts(p, qn, schema),
2170 }
2171}
2172
2173fn derivation_methods_between(
2179 child: &TypeRef,
2180 ancestor: &TypeRef,
2181 schema: &Schema,
2182) -> Option<BlockSet> {
2183 let child = resolve_typeref_via_schema(child, schema);
2184 let ancestor = resolve_typeref_via_schema(ancestor, schema);
2185 if type_refs_equal(&child, &ancestor) {
2186 return Some(BlockSet::empty());
2187 }
2188 if is_any_type(&ancestor) {
2189 return Some(BlockSet::RESTRICTION);
2190 }
2191 if is_any_simple_type(&ancestor) {
2192 return match &child {
2193 TypeRef::Simple(_) => Some(BlockSet::RESTRICTION),
2194 TypeRef::Complex(_) => None,
2195 };
2196 }
2197 match &child {
2198 TypeRef::Complex(ct) => {
2199 let mut methods = BlockSet::empty();
2200 let mut cur: Arc<ComplexType> = ct.clone();
2201 for _ in 0..64 {
2202 let d = cur.derivation.as_ref()?;
2203 methods |= match d.method {
2204 DerivationMethod::Restriction => BlockSet::RESTRICTION,
2205 DerivationMethod::Extension => BlockSet::EXTENSION,
2206 };
2207 let resolved_base = resolve_typeref_via_schema(&d.base, schema);
2208 if type_refs_equal(&resolved_base, &ancestor) {
2209 return Some(methods);
2210 }
2211 match resolved_base {
2212 TypeRef::Complex(next) => { cur = next; }
2213 TypeRef::Simple(_) => return None,
2214 }
2215 }
2216 None
2217 }
2218 TypeRef::Simple(s_child) => {
2219 if let TypeRef::Simple(s_anc) = &ancestor {
2220 if s_child.builtin == s_anc.builtin {
2221 Some(BlockSet::RESTRICTION)
2222 } else { None }
2223 } else { None }
2224 }
2225 }
2226}
2227
2228fn resolve_typeref_via_schema(tr: &TypeRef, schema: &Schema) -> TypeRef {
2229 if let TypeRef::Simple(st) = tr {
2230 if let Some(name) = &st.name {
2231 if let Some(rest) = name.strip_prefix("UNRESOLVED:") {
2232 let qn = parse_unresolved_marker(rest);
2233 if let Some(real) = schema.type_def(&qn) {
2234 return real.clone();
2235 }
2236 }
2237 }
2238 }
2239 tr.clone()
2240}
2241
2242fn wildcard_accepts_qname(
2247 wc: &Wildcard,
2248 qn: &QName,
2249 schema: &Schema,
2250 siblings: &[QName],
2251) -> bool {
2252 super::dfa::wildcard_admits(
2253 wc, qn, schema.target_namespace(),
2254 |q| schema.element(q).is_some(),
2255 |q| siblings.iter().any(|s| s == q),
2256 )
2257}
2258
2259fn attr_wildcard_accepts_qname(
2264 wc: &Wildcard,
2265 qn: &QName,
2266 schema: &Schema,
2267 ct: &ComplexType,
2268) -> bool {
2269 super::dfa::wildcard_admits(
2270 wc, qn, schema.target_namespace(),
2271 |q| schema.attribute(q).is_some(),
2272 |q| ct.attributes.iter().any(|au| &au.decl.name == q),
2273 )
2274}
2275
2276fn outcome_for(p: &Particle, qn: &QName, schema: &Schema) -> MatchOutcome {
2277 match &p.term {
2278 Term::Element(decl) => {
2279 if &decl.name == qn {
2281 return MatchOutcome::Element(decl.clone());
2282 }
2283 if !decl.block.contains(super::schema::BlockSet::SUBSTITUTION) {
2287 for sub in schema.substitutes_for(&decl.name) {
2288 if &sub.name == qn {
2289 let methods = derivation_methods_between(
2296 &sub.type_def, &decl.type_def, schema,
2297 );
2298 if let Some(m) = methods {
2299 if !(decl.block & m).is_empty() {
2300 return MatchOutcome::Element(decl.clone());
2301 }
2302 }
2303 return MatchOutcome::Element(sub.clone());
2304 }
2305 }
2306 }
2307 MatchOutcome::Element(decl.clone())
2308 }
2309 Term::Wildcard(wc) => MatchOutcome::Wildcard(wc.clone()),
2310 Term::Group { particles, .. } => {
2311 for p in particles.iter() {
2313 if particle_accepts(p, qn, schema) {
2314 return outcome_for(p, qn, schema);
2315 }
2316 }
2317 MatchOutcome::None
2318 }
2319 Term::GroupRef(_) => MatchOutcome::None,
2320 }
2321}
2322
2323fn selector_matches<'s>(
2334 sel: &SelectorPath,
2335 stack: &[ElementCtx<'s>],
2336 qn: &QName,
2337 rel_depth: usize,
2338) -> bool {
2339 sel.paths.iter().any(|p| path_matches(p, stack, qn, rel_depth))
2340}
2341
2342fn path_matches<'s>(
2343 p: &PathExpr,
2344 stack: &[ElementCtx<'s>],
2345 qn: &QName,
2346 rel_depth: usize,
2347) -> bool {
2348 let n = p.steps.len();
2349 if n == 0 {
2350 return rel_depth == 0;
2352 }
2353 if p.descendant {
2354 if rel_depth < n { return false; }
2356 } else {
2357 if rel_depth != n { return false; }
2359 }
2360 let stack_top = stack.len();
2365 for (k, step) in p.steps.iter().rev().enumerate() {
2366 let nm = match step {
2367 PathStep::Child(nm) | PathStep::Attribute(nm) => nm,
2368 };
2369 let target_qn: &QName = if k == 0 {
2370 qn
2371 } else {
2372 &stack[stack_top - k].name
2373 };
2374 if !name_test_matches_qname(nm, target_qn) {
2375 return false;
2376 }
2377 }
2378 true
2379}
2380
2381fn name_test_matches_qname(nt: &NameTest, qn: &QName) -> bool {
2382 match nt {
2383 NameTest::Any => true,
2384 NameTest::Name(want) => {
2390 want.local == qn.local
2391 && (want.namespace.is_none() || want.namespace == qn.namespace)
2392 }
2393 NameTest::AnyInNs(ns) => qn.namespace.as_deref() == Some(ns.as_ref()),
2394 }
2395}
2396
2397enum FieldEval {
2412 Missing,
2415 Single(String),
2417 Ambiguous,
2419}
2420
2421fn eval_field(
2422 fp: &FieldPath,
2423 cached_attrs: &[(QName, String)],
2424 text_buf: &str,
2425 children: &[ChildSnapshot],
2426) -> FieldEval {
2427 for path in &fp.paths {
2432 match eval_path_steps(&path.steps, cached_attrs, text_buf, children) {
2433 FieldEval::Single(v) => return FieldEval::Single(v),
2434 FieldEval::Ambiguous => return FieldEval::Ambiguous,
2435 FieldEval::Missing => continue,
2436 }
2437 }
2438 FieldEval::Missing
2439}
2440
2441fn eval_path_steps(
2448 steps: &[PathStep],
2449 attrs: &[(QName, String)],
2450 text: &str,
2451 children: &[ChildSnapshot],
2452) -> FieldEval {
2453 match steps {
2454 [] => FieldEval::Single(text.trim().to_string()),
2455 [PathStep::Attribute(nm)] => match lookup_attr(attrs, nm) {
2456 Some(v) => FieldEval::Single(v),
2457 None => FieldEval::Missing,
2458 },
2459 [PathStep::Child(nm), rest @ ..] => {
2460 let mut matched: Option<&ChildSnapshot> = None;
2461 for c in children {
2462 if name_test_matches_qname(nm, &c.name) {
2463 if matched.is_some() {
2464 return FieldEval::Ambiguous;
2465 }
2466 matched = Some(c);
2467 }
2468 }
2469 match matched {
2470 Some(c) => eval_path_steps(rest, &c.attrs, &c.text, &c.children),
2471 None => FieldEval::Missing,
2472 }
2473 }
2474 _ => FieldEval::Missing,
2478 }
2479}
2480
2481fn lookup_attr(attrs: &[(QName, String)], nt: &NameTest) -> Option<String> {
2482 match nt {
2483 NameTest::Any => attrs.first().map(|(_, v)| v.clone()),
2484 NameTest::Name(want) => attrs.iter()
2485 .find(|(qn, _)| {
2486 qn.local == want.local
2489 && (want.namespace.is_none() || want.namespace == qn.namespace)
2490 })
2491 .map(|(_, v)| v.clone()),
2492 NameTest::AnyInNs(ns) => attrs.iter()
2493 .find(|(qn, _)| qn.namespace.as_deref() == Some(ns.as_ref()))
2494 .map(|(_, v)| v.clone()),
2495 }
2496}
2497
2498impl<'s, 'x, E: XsdEventSource<'x>> Validator<'s, 'x, E> {
2499 fn finalize_key_scope(&mut self, scope: &KeyScope) {
2502 let off = scope.declaring_offset as usize;
2507 for (ci, c) in scope.decl.identity.iter().enumerate() {
2508 let tuples = &scope.collected[ci];
2509 match c.kind {
2510 ConstraintKind::Key | ConstraintKind::Unique => {
2511 let mut seen: HashMap<&KeyTuple, ()> = HashMap::default();
2517 for t in tuples {
2518 let has_null = t.iter().any(|f| f.is_none());
2519 if has_null {
2520 if c.kind == ConstraintKind::Key {
2521 self.report_at(
2522 ValidationKind::Other,
2523 format!(
2524 "<xs:key {:?}>: a selected element is missing one of the field values",
2525 c.name.local
2526 ),
2527 off,
2528 );
2529 }
2530 continue;
2532 }
2533 if seen.insert(t, ()).is_some() {
2534 let pretty = format_tuple(t);
2535 self.report_at(
2536 ValidationKind::KeyNotUnique,
2537 format!(
2538 "<xs:{} {:?}>: duplicate key value {pretty}",
2539 if c.kind == ConstraintKind::Key { "key" } else { "unique" },
2540 c.name.local
2541 ),
2542 off,
2543 );
2544 }
2545 }
2546 }
2547 ConstraintKind::KeyRef => {
2548 let refer = match &c.refer {
2549 Some(r) => r,
2550 None => continue, };
2552 let dangling: Vec<KeyTuple> = {
2560 let referenced: Option<&Vec<KeyTuple>> = scope.decl.identity.iter()
2561 .position(|other| &other.name == refer)
2562 .map(|i| &scope.collected[i])
2563 .or_else(|| {
2564 for outer in self.key_scopes.iter().rev() {
2565 if let Some(i) = outer.decl.identity.iter()
2566 .position(|other| &other.name == refer)
2567 {
2568 return Some(&outer.collected[i]);
2569 }
2570 }
2571 None
2572 });
2573 match referenced {
2574 None => {
2575 self.report_at(
2576 ValidationKind::Other,
2577 format!(
2578 "<xs:keyref {:?}>: refer={refer} not found in scope",
2579 c.name.local
2580 ),
2581 off,
2582 );
2583 continue;
2584 }
2585 Some(target) => tuples.iter()
2591 .filter(|t| t.iter().all(|f| f.is_some()))
2592 .filter(|t| !target.contains(t))
2593 .cloned()
2594 .collect(),
2595 }
2596 };
2597 for t in dangling {
2598 let pretty = format_tuple(&t);
2599 self.report_at(
2600 ValidationKind::KeyRefDangling,
2601 format!(
2602 "<xs:keyref {:?}>: value {pretty} has no matching key",
2603 c.name.local
2604 ),
2605 off,
2606 );
2607 }
2608 }
2609 }
2610 }
2611 }
2612}
2613
2614impl<'s, 'x, E: XsdEventSource<'x>> Validator<'s, 'x, E> {
2617 fn derivation_methods_from(&self, child: &TypeRef, ancestor: &TypeRef) -> Option<BlockSet> {
2648 let child = self.resolve_type(child);
2649 let ancestor = self.resolve_type(ancestor);
2650 if type_refs_equal(&child, &ancestor) {
2651 return Some(BlockSet::empty());
2652 }
2653 if is_any_type(&ancestor) {
2661 return Some(BlockSet::RESTRICTION);
2662 }
2663 if is_any_simple_type(&ancestor) {
2664 return match &child {
2665 TypeRef::Simple(_) => Some(BlockSet::RESTRICTION),
2666 TypeRef::Complex(_) => None,
2667 };
2668 }
2669 match &child {
2670 TypeRef::Complex(ct) => {
2671 let mut methods = BlockSet::empty();
2672 let mut cur: Arc<ComplexType> = ct.clone();
2673 for _ in 0..64 {
2676 let d = cur.derivation.as_ref()?;
2677 methods |= match d.method {
2678 DerivationMethod::Restriction => BlockSet::RESTRICTION,
2679 DerivationMethod::Extension => BlockSet::EXTENSION,
2680 };
2681 let resolved_base = self.resolve_type(&d.base);
2682 if type_refs_equal(&resolved_base, &ancestor) {
2683 return Some(methods);
2684 }
2685 match resolved_base {
2686 TypeRef::Complex(next) => { cur = next; }
2687 TypeRef::Simple(_) => return None,
2688 }
2689 }
2690 None
2691 }
2692 TypeRef::Simple(s_child) => {
2693 if let TypeRef::Simple(s_anc) = &ancestor {
2694 if s_child.builtin.derives_from(s_anc.builtin) {
2697 return Some(BlockSet::RESTRICTION);
2698 }
2699 if s_child.name.as_deref() == s_anc.name.as_deref()
2702 && s_child.name.is_some()
2703 {
2704 return Some(BlockSet::empty());
2705 }
2706 use super::types::Variety;
2711 if let Variety::Union { members } = &s_anc.variety {
2712 for m in members {
2713 if self.derivation_methods_from(
2714 &TypeRef::Simple(s_child.clone()),
2715 &TypeRef::Simple(m.clone()),
2716 ).is_some() {
2717 return Some(BlockSet::RESTRICTION);
2718 }
2719 }
2720 }
2721 }
2722 None
2723 }
2724 }
2725 }
2726}
2727
2728fn is_any_type(t: &TypeRef) -> bool {
2732 match t {
2733 TypeRef::Complex(ct) => ct.name.as_ref()
2734 .is_some_and(|n| n.namespace.as_deref() == Some(QName::XSD_NS)
2735 && n.local.as_ref() == "anyType"),
2736 TypeRef::Simple(st) => is_xsd_placeholder(st, "anyType"),
2737 }
2738}
2739
2740fn is_any_simple_type(t: &TypeRef) -> bool {
2752 match t {
2753 TypeRef::Simple(st) => is_xsd_placeholder(st, "anySimpleType")
2754 || st.name.as_deref() == Some("anySimpleType")
2755 || matches!(st.builtin, super::types::BuiltinType::AnySimpleType),
2756 _ => false,
2757 }
2758}
2759
2760fn is_xsd_placeholder(st: &Arc<SimpleType>, local: &str) -> bool {
2761 st.name.as_deref()
2762 .and_then(|n| n.strip_prefix("UNRESOLVED:"))
2763 .map(parse_unresolved_marker)
2764 .is_some_and(|qn| qn.namespace.as_deref() == Some(QName::XSD_NS)
2765 && qn.local.as_ref() == local)
2766}
2767
2768fn type_refs_equal(a: &TypeRef, b: &TypeRef) -> bool {
2773 match (a, b) {
2774 (TypeRef::Complex(x), TypeRef::Complex(y)) => {
2775 Arc::ptr_eq(x, y) || (x.name.is_some() && x.name == y.name)
2776 }
2777 (TypeRef::Simple(x), TypeRef::Simple(y)) => {
2778 Arc::ptr_eq(x, y)
2779 || (x.builtin == y.builtin && x.name == y.name)
2780 }
2781 _ => false,
2782 }
2783}
2784
2785fn format_block_set(b: BlockSet) -> String {
2788 let mut parts: Vec<&str> = Vec::new();
2789 if b.contains(BlockSet::RESTRICTION) { parts.push("restriction"); }
2790 if b.contains(BlockSet::EXTENSION) { parts.push("extension"); }
2791 if b.contains(BlockSet::SUBSTITUTION) { parts.push("substitution"); }
2792 parts.join(" ")
2793}
2794
2795fn field_path_is_dot(fp: &super::identity::FieldPath) -> bool {
2802 fp.paths.iter().all(|p| p.steps.is_empty())
2803}
2804
2805fn canonical_field_key(raw: &str, simple_type: Option<&Arc<SimpleType>>) -> String {
2822 use super::types::{BuiltinType, Value, parse_lexical};
2823 let Some(st) = simple_type else { return raw.to_string(); };
2824 let prim = st.builtin.primitive();
2825 let tag = prim.name();
2826 let prepared: std::borrow::Cow<'_, str> = match st.whitespace {
2828 super::WhitespaceMode::Preserve => std::borrow::Cow::Borrowed(raw),
2829 super::WhitespaceMode::Replace =>
2830 std::borrow::Cow::Owned(raw.chars().map(|c| match c {
2831 '\t' | '\n' | '\r' => ' ',
2832 _ => c,
2833 }).collect()),
2834 super::WhitespaceMode::Collapse =>
2835 std::borrow::Cow::Owned(raw.split_whitespace().collect::<Vec<_>>().join(" ")),
2836 };
2837 let parsed = parse_lexical(st.builtin, &prepared);
2838 let body: String = match parsed {
2839 Ok(Value::Bool(b)) => if b { "true".into() } else { "false".into() },
2840 Ok(Value::Decimal(d)) => d.normalize().to_string(),
2841 Ok(Value::Int(n)) => n.to_string(),
2842 Ok(Value::BigInt(b)) => {
2843 let sign = if b.negative { "-" } else { "" };
2844 format!("{sign}{}", b.digits)
2845 }
2846 Ok(Value::Float(f)) => format!("{f:?}"),
2847 Ok(Value::Double(d)) => format!("{d:?}"),
2848 Ok(Value::String(s)) => s,
2849 Ok(Value::Token(t)) => t,
2850 Ok(Value::Bytes(bs)) => bs.iter().map(|b| format!("{b:02X}")).collect(),
2851 Ok(Value::DateTime(dt)) => format!("{dt:?}"),
2852 Ok(Value::Date(d)) => format!("{d:?}"),
2853 Ok(Value::Time(t)) => format!("{t:?}"),
2854 Ok(Value::GYearMonth(g)) => format!("{g:?}"),
2855 Ok(Value::GYear(g)) => format!("{g:?}"),
2856 Ok(Value::GMonthDay(g)) => format!("{g:?}"),
2857 Ok(Value::GDay(g)) => format!("{g:?}"),
2858 Ok(Value::GMonth(g)) => format!("{g:?}"),
2859 Ok(Value::Duration(d)) => format!("{d:?}"),
2860 Err(_) => prepared.to_string(),
2863 };
2864 let _ = BuiltinType::AnySimpleType; format!("{tag}:{body}")
2866}
2867
2868fn format_tuple(t: &KeyTuple) -> String {
2869 let mut s = String::from("(");
2870 for (i, v) in t.iter().enumerate() {
2871 if i > 0 { s.push_str(", "); }
2872 match v {
2873 Some(v) => { s.push('"'); s.push_str(v); s.push('"'); }
2874 None => s.push_str("<null>"),
2875 }
2876 }
2877 s.push(')');
2878 s
2879}
2880
2881#[cfg(test)]
2884mod tests {
2885 use super::*;
2886
2887 fn xsd_str(extra_decls: &str) -> String {
2888 format!(
2889 r#"<?xml version="1.0"?>
2890<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2891 targetNamespace="urn:test"
2892 xmlns="urn:test"
2893 elementFormDefault="qualified">
2894{extra_decls}
2895</xs:schema>"#
2896 )
2897 }
2898
2899 fn instance_ns() -> &'static str { r#"xmlns="urn:test""# }
2900
2901 #[test]
2902 fn validate_simple_string() {
2903 let s = Schema::compile_str(&xsd_str(
2904 r#"<xs:element name="msg" type="xs:string"/>"#
2905 )).unwrap();
2906 s.validate_str(&format!(r#"<msg {ns}>hello</msg>"#, ns = instance_ns())).unwrap();
2907 }
2908
2909 #[test]
2910 fn validate_typed_int_value() {
2911 let s = Schema::compile_str(&xsd_str(
2912 r#"<xs:element name="age" type="xs:int"/>"#
2913 )).unwrap();
2914 assert!(s.validate_str(&format!(r#"<age {}>42</age>"#, instance_ns())).is_ok());
2915 assert!(s.validate_str(&format!(r#"<age {}>not-an-int</age>"#, instance_ns())).is_err());
2916 }
2917
2918 #[test]
2919 fn validate_facet_pattern() {
2920 let s = Schema::compile_str(&xsd_str(r#"
2921 <xs:element name="zip" type="ZipCode"/>
2922 <xs:simpleType name="ZipCode">
2923 <xs:restriction base="xs:string">
2924 <xs:pattern value="\d{5}(-\d{4})?"/>
2925 </xs:restriction>
2926 </xs:simpleType>
2927 "#)).unwrap();
2928 let ns = instance_ns();
2929 assert!(s.validate_str(&format!(r#"<zip {ns}>12345</zip>"#)).is_ok());
2930 assert!(s.validate_str(&format!(r#"<zip {ns}>12345-6789</zip>"#)).is_ok());
2931 assert!(s.validate_str(&format!(r#"<zip {ns}>not-a-zip</zip>"#)).is_err());
2932 }
2933
2934 #[test]
2935 fn validate_complex_sequence() {
2936 let s = Schema::compile_str(&xsd_str(r#"
2937 <xs:element name="person" type="Person"/>
2938 <xs:complexType name="Person">
2939 <xs:sequence>
2940 <xs:element name="name" type="xs:string"/>
2941 <xs:element name="age" type="xs:int"/>
2942 </xs:sequence>
2943 </xs:complexType>
2944 "#)).unwrap();
2945 let ns = instance_ns();
2946 assert!(s.validate_str(&format!(
2947 r#"<person {ns}><name>Ada</name><age>30</age></person>"#
2948 )).is_ok());
2949 assert!(s.validate_str(&format!(
2951 r#"<person {ns}><age>30</age><name>Ada</name></person>"#
2952 )).is_err());
2953 }
2954
2955 #[test]
2956 fn validate_required_attribute() {
2957 let s = Schema::compile_str(&xsd_str(r#"
2958 <xs:element name="thing" type="Thing"/>
2959 <xs:complexType name="Thing">
2960 <xs:attribute name="id" type="xs:int" use="required"/>
2961 </xs:complexType>
2962 "#)).unwrap();
2963 let ns = instance_ns();
2964 assert!(s.validate_str(&format!(r#"<thing {ns} id="1"/>"#)).is_ok());
2965 assert!(s.validate_str(&format!(r#"<thing {ns}/>"#)).is_err());
2966 }
2967
2968 #[test]
2969 fn validate_attribute_type() {
2970 let s = Schema::compile_str(&xsd_str(r#"
2971 <xs:element name="thing" type="Thing"/>
2972 <xs:complexType name="Thing">
2973 <xs:attribute name="age" type="xs:int" use="required"/>
2974 </xs:complexType>
2975 "#)).unwrap();
2976 let ns = instance_ns();
2977 assert!(s.validate_str(&format!(r#"<thing {ns} age="not-int"/>"#)).is_err());
2978 }
2979
2980 #[test]
2981 fn validate_max_occurs() {
2982 let s = Schema::compile_str(&xsd_str(r#"
2983 <xs:element name="items" type="Items"/>
2984 <xs:complexType name="Items">
2985 <xs:sequence>
2986 <xs:element name="item" type="xs:string" maxOccurs="3"/>
2987 </xs:sequence>
2988 </xs:complexType>
2989 "#)).unwrap();
2990 let ns = instance_ns();
2991 assert!(s.validate_str(&format!(
2992 r#"<items {ns}><item>a</item><item>b</item><item>c</item></items>"#
2993 )).is_ok());
2994 assert!(s.validate_str(&format!(
2995 r#"<items {ns}><item>a</item><item>b</item><item>c</item><item>d</item></items>"#
2996 )).is_err());
2997 }
2998
2999 #[test]
3000 fn validate_min_occurs() {
3001 let s = Schema::compile_str(&xsd_str(r#"
3002 <xs:element name="items" type="Items"/>
3003 <xs:complexType name="Items">
3004 <xs:sequence>
3005 <xs:element name="item" type="xs:string"
3006 minOccurs="2" maxOccurs="unbounded"/>
3007 </xs:sequence>
3008 </xs:complexType>
3009 "#)).unwrap();
3010 let ns = instance_ns();
3011 assert!(s.validate_str(&format!(
3012 r#"<items {ns}><item>a</item><item>b</item></items>"#
3013 )).is_ok());
3014 assert!(s.validate_str(&format!(
3015 r#"<items {ns}><item>a</item></items>"#
3016 )).is_err());
3017 }
3018
3019 #[test]
3020 fn validate_unbounded() {
3021 let s = Schema::compile_str(&xsd_str(r#"
3022 <xs:element name="items" type="Items"/>
3023 <xs:complexType name="Items">
3024 <xs:sequence>
3025 <xs:element name="item" type="xs:string" maxOccurs="unbounded"/>
3026 </xs:sequence>
3027 </xs:complexType>
3028 "#)).unwrap();
3029 let ns = instance_ns();
3030 let mut xml = format!(r#"<items {ns}>"#);
3031 for _ in 0..50 { xml.push_str("<item>x</item>"); }
3032 xml.push_str("</items>");
3033 assert!(s.validate_str(&xml).is_ok());
3034 }
3035
3036 #[test]
3037 fn validate_choice() {
3038 let s = Schema::compile_str(&xsd_str(r#"
3039 <xs:element name="either" type="Either"/>
3040 <xs:complexType name="Either">
3041 <xs:choice>
3042 <xs:element name="left" type="xs:int"/>
3043 <xs:element name="right" type="xs:string"/>
3044 </xs:choice>
3045 </xs:complexType>
3046 "#)).unwrap();
3047 let ns = instance_ns();
3048 assert!(s.validate_str(&format!(r#"<either {ns}><left>1</left></either>"#)).is_ok());
3049 assert!(s.validate_str(&format!(r#"<either {ns}><right>hi</right></either>"#)).is_ok());
3050 assert!(s.validate_str(&format!(r#"<either {ns}/>"#)).is_err());
3052 }
3053
3054 #[test]
3055 fn validate_all_any_order() {
3056 let s = Schema::compile_str(&xsd_str(r#"
3057 <xs:element name="both" type="Both"/>
3058 <xs:complexType name="Both">
3059 <xs:all>
3060 <xs:element name="a" type="xs:int"/>
3061 <xs:element name="b" type="xs:int"/>
3062 </xs:all>
3063 </xs:complexType>
3064 "#)).unwrap();
3065 let ns = instance_ns();
3066 assert!(s.validate_str(&format!(r#"<both {ns}><a>1</a><b>2</b></both>"#)).is_ok());
3067 assert!(s.validate_str(&format!(r#"<both {ns}><b>2</b><a>1</a></both>"#)).is_ok());
3068 assert!(s.validate_str(&format!(r#"<both {ns}><a>1</a></both>"#)).is_err());
3070 }
3071
3072 #[test]
3073 fn validate_xsi_nil() {
3074 let s = Schema::compile_str(&xsd_str(
3075 r#"<xs:element name="opt" type="xs:int" nillable="true"/>"#
3076 )).unwrap();
3077 let inst = format!(
3078 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true"/>"#,
3079 ns = instance_ns(), xsi = XSI_NS,
3080 );
3081 assert!(s.validate_str(&inst).is_ok());
3082 }
3083
3084 #[test]
3085 fn validate_xsi_nil_on_non_nillable_fails() {
3086 let s = Schema::compile_str(&xsd_str(
3087 r#"<xs:element name="opt" type="xs:int"/>"#
3088 )).unwrap();
3089 let inst = format!(
3090 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true"/>"#,
3091 ns = instance_ns(), xsi = XSI_NS,
3092 );
3093 assert!(s.validate_str(&inst).is_err());
3094 }
3095
3096 #[test]
3097 fn validate_unknown_root_fails() {
3098 let s = Schema::compile_str(&xsd_str(
3099 r#"<xs:element name="known" type="xs:string"/>"#
3100 )).unwrap();
3101 let ns = instance_ns();
3102 assert!(s.validate_str(&format!(r#"<unknown {ns}>x</unknown>"#)).is_err());
3103 }
3104
3105 #[test]
3106 fn validate_collects_multiple_issues_when_not_fail_fast() {
3107 let s = Schema::compile_str(&xsd_str(r#"
3108 <xs:element name="thing" type="Thing"/>
3109 <xs:complexType name="Thing">
3110 <xs:attribute name="id" type="xs:int" use="required"/>
3111 <xs:attribute name="name" type="xs:string" use="required"/>
3112 </xs:complexType>
3113 "#)).unwrap();
3114 let inst = format!(r#"<thing {}/>"#, instance_ns());
3115 let opts = ValidationOptions { fail_fast: false, max_issues: 100, ..Default::default() };
3116 let err = s.validate_str_opts(&inst, opts).unwrap_err();
3117 assert!(err.issues.len() >= 2);
3118 }
3119
3120 fn parts_xsd() -> String {
3123 xsd_str(r#"
3127 <xs:element name="catalog">
3128 <xs:complexType>
3129 <xs:sequence>
3130 <xs:element name="parts">
3131 <xs:complexType>
3132 <xs:sequence>
3133 <xs:element name="part" maxOccurs="unbounded">
3134 <xs:complexType>
3135 <xs:attribute name="num" type="xs:string" use="required"/>
3136 </xs:complexType>
3137 </xs:element>
3138 </xs:sequence>
3139 </xs:complexType>
3140 </xs:element>
3141 <xs:element name="orders">
3142 <xs:complexType>
3143 <xs:sequence>
3144 <xs:element name="line" maxOccurs="unbounded">
3145 <xs:complexType>
3146 <xs:attribute name="part" type="xs:string" use="required"/>
3147 </xs:complexType>
3148 </xs:element>
3149 </xs:sequence>
3150 </xs:complexType>
3151 </xs:element>
3152 </xs:sequence>
3153 </xs:complexType>
3154 <xs:key name="partKey">
3155 <xs:selector xpath=".//part"/>
3156 <xs:field xpath="@num"/>
3157 </xs:key>
3158 <xs:keyref name="lineRef" refer="partKey">
3159 <xs:selector xpath=".//line"/>
3160 <xs:field xpath="@part"/>
3161 </xs:keyref>
3162 </xs:element>
3163 "#)
3164 }
3165
3166 #[test]
3167 fn xs_key_accepts_unique_values() {
3168 let s = Schema::compile_str(&parts_xsd()).unwrap();
3169 let xml = format!(
3170 r#"<catalog {ns}>
3171 <parts>
3172 <part num="A1"/><part num="A2"/><part num="A3"/>
3173 </parts>
3174 <orders>
3175 <line part="A1"/><line part="A3"/>
3176 </orders>
3177 </catalog>"#,
3178 ns = instance_ns(),
3179 );
3180 s.validate_str(&xml).unwrap();
3181 }
3182
3183 #[test]
3184 fn xs_key_rejects_duplicates() {
3185 let s = Schema::compile_str(&parts_xsd()).unwrap();
3186 let xml = format!(
3187 r#"<catalog {ns}>
3188 <parts>
3189 <part num="A1"/><part num="A1"/>
3190 </parts>
3191 <orders>
3192 <line part="A1"/>
3193 </orders>
3194 </catalog>"#,
3195 ns = instance_ns(),
3196 );
3197 let err = s.validate_str(&xml).unwrap_err();
3198 assert!(err.issues.iter().any(|i|
3199 matches!(i.kind, ValidationKind::KeyNotUnique)
3200 ), "expected KeyNotUnique, got {:?}", err.issues);
3201 }
3202
3203 #[test]
3204 fn xs_keyref_rejects_dangling() {
3205 let s = Schema::compile_str(&parts_xsd()).unwrap();
3206 let xml = format!(
3207 r#"<catalog {ns}>
3208 <parts>
3209 <part num="A1"/>
3210 </parts>
3211 <orders>
3212 <line part="UNKNOWN"/>
3213 </orders>
3214 </catalog>"#,
3215 ns = instance_ns(),
3216 );
3217 let err = s.validate_str(&xml).unwrap_err();
3218 assert!(err.issues.iter().any(|i|
3219 matches!(i.kind, ValidationKind::KeyRefDangling)
3220 ), "expected KeyRefDangling, got {:?}", err.issues);
3221 }
3222
3223 #[test]
3224 fn xs_unique_allows_missing_field() {
3225 let s = Schema::compile_str(&xsd_str(r#"
3227 <xs:element name="root">
3228 <xs:complexType>
3229 <xs:sequence>
3230 <xs:element name="x" maxOccurs="unbounded">
3231 <xs:complexType>
3232 <xs:attribute name="id" type="xs:string"/>
3233 </xs:complexType>
3234 </xs:element>
3235 </xs:sequence>
3236 </xs:complexType>
3237 <xs:unique name="xUnique">
3238 <xs:selector xpath=".//x"/>
3239 <xs:field xpath="@id"/>
3240 </xs:unique>
3241 </xs:element>
3242 "#)).unwrap();
3243 let xml = format!(
3244 r#"<root {ns}><x id="A"/><x/><x id="B"/></root>"#,
3245 ns = instance_ns(),
3246 );
3247 s.validate_str(&xml).unwrap();
3249 let bad = format!(
3251 r#"<root {ns}><x id="A"/><x id="A"/></root>"#,
3252 ns = instance_ns(),
3253 );
3254 assert!(s.validate_str(&bad).is_err());
3255 }
3256
3257 #[test]
3260 fn dfa_rejects_ambiguous_schema_at_compile_time() {
3261 let xsd = xsd_str(r#"
3265 <xs:complexType name="Bad">
3266 <xs:sequence>
3267 <xs:element name="x" type="xs:string" minOccurs="0"/>
3268 <xs:element name="x" type="xs:string"/>
3269 </xs:sequence>
3270 </xs:complexType>
3271 <xs:element name="root" type="Bad"/>
3272 "#);
3273 let err = Schema::compile_str(&xsd).unwrap_err();
3274 assert!(err.message.contains("Unique Particle Attribution"),
3275 "expected UPA error, got {:?}", err.message);
3276 }
3277
3278 #[test]
3279 fn dfa_rejects_ambiguous_choice_at_compile_time() {
3280 let xsd = xsd_str(r#"
3282 <xs:element name="root">
3283 <xs:complexType>
3284 <xs:choice>
3285 <xs:element name="x" type="xs:string"/>
3286 <xs:element name="x" type="xs:int"/>
3287 </xs:choice>
3288 </xs:complexType>
3289 </xs:element>
3290 "#);
3291 assert!(Schema::compile_str(&xsd).is_err());
3292 }
3293
3294 #[test]
3295 fn dfa_error_message_lists_expected_elements() {
3296 let s = Schema::compile_str(&xsd_str(r#"
3298 <xs:element name="root">
3299 <xs:complexType>
3300 <xs:sequence>
3301 <xs:element name="alpha" type="xs:string"/>
3302 <xs:element name="beta" type="xs:string"/>
3303 </xs:sequence>
3304 </xs:complexType>
3305 </xs:element>
3306 "#)).unwrap();
3307 let xml = format!(r#"<root {ns}><alpha>x</alpha></root>"#, ns = instance_ns());
3308 let err = s.validate_str(&xml).unwrap_err();
3309 assert!(err.issues.iter().any(|i| i.message.contains("beta")),
3311 "expected `beta` in error, got {:?}", err.issues);
3312 }
3313
3314 #[test]
3315 fn dfa_handles_substitution_groups() {
3316 let s = Schema::compile_str(&xsd_str(r#"
3319 <xs:element name="figure" type="xs:string" abstract="true"/>
3320 <xs:element name="image" type="xs:string" substitutionGroup="figure"/>
3321 <xs:element name="chart" type="xs:string" substitutionGroup="figure"/>
3322 <xs:element name="report">
3323 <xs:complexType>
3324 <xs:sequence>
3325 <xs:element ref="figure" maxOccurs="unbounded"/>
3326 </xs:sequence>
3327 </xs:complexType>
3328 </xs:element>
3329 "#)).unwrap();
3330 let xml = format!(
3331 r#"<report {ns}><image>i</image><chart>c</chart><image>j</image></report>"#,
3332 ns = instance_ns(),
3333 );
3334 s.validate_str(&xml).unwrap();
3335 }
3336
3337 #[test]
3338 fn dfa_unbounded_works_correctly() {
3339 let s = Schema::compile_str(&xsd_str(r#"
3342 <xs:element name="items">
3343 <xs:complexType>
3344 <xs:sequence>
3345 <xs:element name="item" type="xs:int" maxOccurs="unbounded"/>
3346 </xs:sequence>
3347 </xs:complexType>
3348 </xs:element>
3349 "#)).unwrap();
3350 let ns = instance_ns();
3351 for n in [1, 5, 100, 1000] {
3352 let mut xml = format!(r#"<items {ns}>"#);
3353 for i in 0..n { xml.push_str(&format!("<item>{i}</item>")); }
3354 xml.push_str("</items>");
3355 s.validate_str(&xml)
3356 .unwrap_or_else(|e| panic!("n={n} should validate: {e:?}"));
3357 }
3358 }
3359
3360 #[test]
3361 fn dfa_accepts_optional_followed_by_required() {
3362 let s = Schema::compile_str(&xsd_str(r#"
3366 <xs:element name="msg">
3367 <xs:complexType>
3368 <xs:sequence>
3369 <xs:element name="header" type="xs:string" minOccurs="0"/>
3370 <xs:element name="body" type="xs:string"/>
3371 </xs:sequence>
3372 </xs:complexType>
3373 </xs:element>
3374 "#)).unwrap();
3375 let ns = instance_ns();
3376 s.validate_str(&format!(
3377 r#"<msg {ns}><header>h</header><body>b</body></msg>"#)).unwrap();
3378 s.validate_str(&format!(
3379 r#"<msg {ns}><body>b</body></msg>"#)).unwrap();
3380 assert!(s.validate_str(&format!(
3382 r#"<msg {ns}><header>h</header></msg>"#)).is_err());
3383 }
3384
3385 #[test]
3386 fn xs_key_with_child_attribute_field() {
3387 let s = Schema::compile_str(&xsd_str(r#"
3392 <xs:element name="users">
3393 <xs:complexType>
3394 <xs:sequence>
3395 <xs:element name="user" maxOccurs="unbounded">
3396 <xs:complexType>
3397 <xs:sequence>
3398 <xs:element name="id">
3399 <xs:complexType>
3400 <xs:attribute name="value" type="xs:string" use="required"/>
3401 </xs:complexType>
3402 </xs:element>
3403 </xs:sequence>
3404 </xs:complexType>
3405 </xs:element>
3406 </xs:sequence>
3407 </xs:complexType>
3408 <xs:key name="userKey">
3409 <xs:selector xpath=".//user"/>
3410 <xs:field xpath="id/@value"/>
3411 </xs:key>
3412 </xs:element>
3413 "#)).unwrap();
3414 let ok = format!(
3415 r#"<users {ns}>
3416 <user><id value="alice"/></user>
3417 <user><id value="bob"/></user>
3418 </users>"#,
3419 ns = instance_ns(),
3420 );
3421 s.validate_str(&ok).unwrap();
3422 let dup = format!(
3423 r#"<users {ns}>
3424 <user><id value="alice"/></user>
3425 <user><id value="alice"/></user>
3426 </users>"#,
3427 ns = instance_ns(),
3428 );
3429 let err = s.validate_str(&dup).unwrap_err();
3430 assert!(err.issues.iter().any(|i|
3431 matches!(i.kind, ValidationKind::KeyNotUnique)
3432 ), "expected KeyNotUnique, got {:?}", err.issues);
3433 }
3434
3435 #[test]
3436 fn xs_key_with_child_text_field() {
3437 let s = Schema::compile_str(&xsd_str(r#"
3441 <xs:element name="users">
3442 <xs:complexType>
3443 <xs:sequence>
3444 <xs:element name="user" maxOccurs="unbounded">
3445 <xs:complexType>
3446 <xs:sequence>
3447 <xs:element name="name" type="xs:string"/>
3448 </xs:sequence>
3449 </xs:complexType>
3450 </xs:element>
3451 </xs:sequence>
3452 </xs:complexType>
3453 <xs:key name="userKey">
3454 <xs:selector xpath=".//user"/>
3455 <xs:field xpath="name"/>
3456 </xs:key>
3457 </xs:element>
3458 "#)).unwrap();
3459 let ok = format!(
3460 r#"<users {ns}>
3461 <user><name>alice</name></user>
3462 <user><name>bob</name></user>
3463 </users>"#,
3464 ns = instance_ns(),
3465 );
3466 s.validate_str(&ok).unwrap();
3467 let dup = format!(
3468 r#"<users {ns}>
3469 <user><name>alice</name></user>
3470 <user><name>alice</name></user>
3471 </users>"#,
3472 ns = instance_ns(),
3473 );
3474 let err = s.validate_str(&dup).unwrap_err();
3475 assert!(err.issues.iter().any(|i|
3476 matches!(i.kind, ValidationKind::KeyNotUnique)
3477 ), "expected KeyNotUnique, got {:?}", err.issues);
3478 }
3479
3480 #[test]
3481 fn xs_key_text_field() {
3482 let s = Schema::compile_str(&xsd_str(r#"
3484 <xs:element name="cities">
3485 <xs:complexType>
3486 <xs:sequence>
3487 <xs:element name="city" type="xs:string" maxOccurs="unbounded"/>
3488 </xs:sequence>
3489 </xs:complexType>
3490 <xs:key name="cityKey">
3491 <xs:selector xpath=".//city"/>
3492 <xs:field xpath="."/>
3493 </xs:key>
3494 </xs:element>
3495 "#)).unwrap();
3496 let ok = format!(
3497 r#"<cities {ns}><city>Boston</city><city>NYC</city></cities>"#,
3498 ns = instance_ns(),
3499 );
3500 s.validate_str(&ok).unwrap();
3501 let bad = format!(
3502 r#"<cities {ns}><city>Boston</city><city>Boston</city></cities>"#,
3503 ns = instance_ns(),
3504 );
3505 assert!(s.validate_str(&bad).is_err());
3506 }
3507
3508 #[test]
3511 fn xs_key_with_grandchild_text_field() {
3512 let s = Schema::compile_str(&xsd_str(r#"
3516 <xs:element name="users">
3517 <xs:complexType>
3518 <xs:sequence>
3519 <xs:element name="user" maxOccurs="unbounded">
3520 <xs:complexType>
3521 <xs:sequence>
3522 <xs:element name="name">
3523 <xs:complexType>
3524 <xs:sequence>
3525 <xs:element name="first" type="xs:string"/>
3526 <xs:element name="last" type="xs:string"/>
3527 </xs:sequence>
3528 </xs:complexType>
3529 </xs:element>
3530 </xs:sequence>
3531 </xs:complexType>
3532 </xs:element>
3533 </xs:sequence>
3534 </xs:complexType>
3535 <xs:key name="userKey">
3536 <xs:selector xpath=".//user"/>
3537 <xs:field xpath="name/first"/>
3538 </xs:key>
3539 </xs:element>
3540 "#)).unwrap();
3541 let ok = format!(
3542 r#"<users {ns}>
3543 <user><name><first>Alice</first><last>A</last></name></user>
3544 <user><name><first>Bob</first><last>B</last></name></user>
3545 </users>"#,
3546 ns = instance_ns(),
3547 );
3548 s.validate_str(&ok).unwrap();
3549 let dup = format!(
3550 r#"<users {ns}>
3551 <user><name><first>Alice</first><last>A</last></name></user>
3552 <user><name><first>Alice</first><last>Z</last></name></user>
3553 </users>"#,
3554 ns = instance_ns(),
3555 );
3556 let err = s.validate_str(&dup).unwrap_err();
3557 assert!(err.issues.iter().any(|i|
3558 matches!(i.kind, ValidationKind::KeyNotUnique)
3559 ), "expected KeyNotUnique on duplicate name/first, got {:?}", err.issues);
3560 }
3561
3562 #[test]
3563 fn xs_key_with_grandchild_attribute_field() {
3564 let s = Schema::compile_str(&xsd_str(r#"
3566 <xs:element name="users">
3567 <xs:complexType>
3568 <xs:sequence>
3569 <xs:element name="user" maxOccurs="unbounded">
3570 <xs:complexType>
3571 <xs:sequence>
3572 <xs:element name="name">
3573 <xs:complexType>
3574 <xs:sequence>
3575 <xs:element name="first">
3576 <xs:complexType>
3577 <xs:simpleContent>
3578 <xs:extension base="xs:string">
3579 <xs:attribute name="lang" type="xs:string"/>
3580 </xs:extension>
3581 </xs:simpleContent>
3582 </xs:complexType>
3583 </xs:element>
3584 </xs:sequence>
3585 </xs:complexType>
3586 </xs:element>
3587 </xs:sequence>
3588 </xs:complexType>
3589 </xs:element>
3590 </xs:sequence>
3591 </xs:complexType>
3592 <xs:key name="userKey">
3593 <xs:selector xpath=".//user"/>
3594 <xs:field xpath="name/first/@lang"/>
3595 </xs:key>
3596 </xs:element>
3597 "#)).unwrap();
3598 let ok = format!(
3599 r#"<users {ns}>
3600 <user><name><first lang="en">Alice</first></name></user>
3601 <user><name><first lang="fr">Alphonse</first></name></user>
3602 </users>"#,
3603 ns = instance_ns(),
3604 );
3605 s.validate_str(&ok).unwrap();
3606 let dup = format!(
3607 r#"<users {ns}>
3608 <user><name><first lang="en">Alice</first></name></user>
3609 <user><name><first lang="en">Bob</first></name></user>
3610 </users>"#,
3611 ns = instance_ns(),
3612 );
3613 let err = s.validate_str(&dup).unwrap_err();
3614 assert!(err.issues.iter().any(|i|
3615 matches!(i.kind, ValidationKind::KeyNotUnique)
3616 ), "expected KeyNotUnique on duplicate @lang, got {:?}", err.issues);
3617 }
3618
3619 #[test]
3620 fn xs_keyref_with_deep_field() {
3621 let s = Schema::compile_str(&xsd_str(r#"
3625 <xs:element name="catalog">
3626 <xs:complexType>
3627 <xs:sequence>
3628 <xs:element name="parts">
3629 <xs:complexType>
3630 <xs:sequence>
3631 <xs:element name="part" maxOccurs="unbounded">
3632 <xs:complexType>
3633 <xs:sequence>
3634 <xs:element name="header">
3635 <xs:complexType>
3636 <xs:sequence>
3637 <xs:element name="meta">
3638 <xs:complexType>
3639 <xs:attribute name="sku" type="xs:string"/>
3640 </xs:complexType>
3641 </xs:element>
3642 </xs:sequence>
3643 </xs:complexType>
3644 </xs:element>
3645 </xs:sequence>
3646 </xs:complexType>
3647 </xs:element>
3648 </xs:sequence>
3649 </xs:complexType>
3650 </xs:element>
3651 <xs:element name="orders">
3652 <xs:complexType>
3653 <xs:sequence>
3654 <xs:element name="line" maxOccurs="unbounded">
3655 <xs:complexType>
3656 <xs:sequence>
3657 <xs:element name="header">
3658 <xs:complexType>
3659 <xs:sequence>
3660 <xs:element name="ref">
3661 <xs:complexType>
3662 <xs:attribute name="sku" type="xs:string"/>
3663 </xs:complexType>
3664 </xs:element>
3665 </xs:sequence>
3666 </xs:complexType>
3667 </xs:element>
3668 </xs:sequence>
3669 </xs:complexType>
3670 </xs:element>
3671 </xs:sequence>
3672 </xs:complexType>
3673 </xs:element>
3674 </xs:sequence>
3675 </xs:complexType>
3676 <xs:key name="partKey">
3677 <xs:selector xpath=".//part"/>
3678 <xs:field xpath="header/meta/@sku"/>
3679 </xs:key>
3680 <xs:keyref name="lineRef" refer="partKey">
3681 <xs:selector xpath=".//line"/>
3682 <xs:field xpath="header/ref/@sku"/>
3683 </xs:keyref>
3684 </xs:element>
3685 "#)).unwrap();
3686 let ok = format!(
3687 r#"<catalog {ns}>
3688 <parts>
3689 <part><header><meta sku="A1"/></header></part>
3690 <part><header><meta sku="A2"/></header></part>
3691 </parts>
3692 <orders>
3693 <line><header><ref sku="A1"/></header></line>
3694 <line><header><ref sku="A2"/></header></line>
3695 </orders>
3696 </catalog>"#,
3697 ns = instance_ns(),
3698 );
3699 s.validate_str(&ok).unwrap();
3700 let bad = format!(
3701 r#"<catalog {ns}>
3702 <parts>
3703 <part><header><meta sku="A1"/></header></part>
3704 </parts>
3705 <orders>
3706 <line><header><ref sku="A1"/></header></line>
3707 <line><header><ref sku="UNKNOWN"/></header></line>
3708 </orders>
3709 </catalog>"#,
3710 ns = instance_ns(),
3711 );
3712 let err = s.validate_str(&bad).unwrap_err();
3713 assert!(err.issues.iter().any(|i|
3714 matches!(i.kind, ValidationKind::KeyRefDangling)
3715 ), "expected KeyRefDangling on unknown sku, got {:?}", err.issues);
3716 }
3717
3718 #[test]
3719 fn xs_unique_three_level_child_path() {
3720 let s = Schema::compile_str(&xsd_str(r#"
3722 <xs:element name="root">
3723 <xs:complexType>
3724 <xs:sequence>
3725 <xs:element name="item" maxOccurs="unbounded">
3726 <xs:complexType>
3727 <xs:sequence>
3728 <xs:element name="a">
3729 <xs:complexType>
3730 <xs:sequence>
3731 <xs:element name="b">
3732 <xs:complexType>
3733 <xs:sequence>
3734 <xs:element name="c" type="xs:string"/>
3735 </xs:sequence>
3736 </xs:complexType>
3737 </xs:element>
3738 </xs:sequence>
3739 </xs:complexType>
3740 </xs:element>
3741 </xs:sequence>
3742 </xs:complexType>
3743 </xs:element>
3744 </xs:sequence>
3745 </xs:complexType>
3746 <xs:unique name="itemUnique">
3747 <xs:selector xpath=".//item"/>
3748 <xs:field xpath="a/b/c"/>
3749 </xs:unique>
3750 </xs:element>
3751 "#)).unwrap();
3752 let ok = format!(
3753 r#"<root {ns}>
3754 <item><a><b><c>x</c></b></a></item>
3755 <item><a><b><c>y</c></b></a></item>
3756 </root>"#,
3757 ns = instance_ns(),
3758 );
3759 s.validate_str(&ok).unwrap();
3760 let dup = format!(
3761 r#"<root {ns}>
3762 <item><a><b><c>x</c></b></a></item>
3763 <item><a><b><c>x</c></b></a></item>
3764 </root>"#,
3765 ns = instance_ns(),
3766 );
3767 let err = s.validate_str(&dup).unwrap_err();
3768 assert!(err.issues.iter().any(|i|
3769 matches!(i.kind, ValidationKind::KeyNotUnique)
3770 ), "expected KeyNotUnique on duplicate a/b/c, got {:?}", err.issues);
3771 }
3772
3773 #[test]
3774 fn xs_key_deep_path_missing_intermediate_is_missing_field() {
3775 let s = Schema::compile_str(&xsd_str(r#"
3779 <xs:element name="users">
3780 <xs:complexType>
3781 <xs:sequence>
3782 <xs:element name="user" maxOccurs="unbounded">
3783 <xs:complexType>
3784 <xs:sequence>
3785 <xs:element name="name" minOccurs="0">
3786 <xs:complexType>
3787 <xs:sequence>
3788 <xs:element name="first" type="xs:string" minOccurs="0"/>
3789 </xs:sequence>
3790 </xs:complexType>
3791 </xs:element>
3792 </xs:sequence>
3793 </xs:complexType>
3794 </xs:element>
3795 </xs:sequence>
3796 </xs:complexType>
3797 <xs:key name="userKey">
3798 <xs:selector xpath=".//user"/>
3799 <xs:field xpath="name/first"/>
3800 </xs:key>
3801 </xs:element>
3802 "#)).unwrap();
3803 let bad = format!(
3807 r#"<users {ns}>
3808 <user><name><first>Alice</first></name></user>
3809 <user/>
3810 </users>"#,
3811 ns = instance_ns(),
3812 );
3813 let err = s.validate_str(&bad).unwrap_err();
3814 let missing: Vec<&ValidationIssue> = err.issues.iter()
3815 .filter(|i| i.message.contains("missing one of the field values"))
3816 .collect();
3817 assert_eq!(missing.len(), 1,
3820 "expected exactly one missing-field error, got {:?}", err.issues);
3821 }
3822
3823 #[test]
3826 fn issue_carries_line_and_column_for_simple_content() {
3827 let s = Schema::compile_str(&xsd_str(
3828 r#"<xs:element name="age" type="xs:int"/>"#
3829 )).unwrap();
3830 let bad = format!(r#"<age {}>not-an-int</age>"#, instance_ns());
3831 let err = s.validate_str(&bad).unwrap_err();
3832 let issue = &err.issues[0];
3833 assert_eq!(issue.line, Some(1), "single-line input, expected line 1, got {issue:?}");
3834 assert!(issue.column.is_some(), "expected column to be filled, got {issue:?}");
3835 }
3836
3837 #[test]
3838 fn issue_line_points_at_offending_element_in_multiline_input() {
3839 let s = Schema::compile_str(&xsd_str(r#"
3840 <xs:element name="r">
3841 <xs:complexType>
3842 <xs:sequence>
3843 <xs:element name="bad" type="xs:int"/>
3844 </xs:sequence>
3845 </xs:complexType>
3846 </xs:element>
3847 "#)).unwrap();
3848 let bad = format!("<r {}>\n \n \n <bad>not-an-int</bad>\n</r>", instance_ns());
3850 let err = s.validate_str(&bad).unwrap_err();
3851 let issue = err.issues.iter()
3852 .find(|i| i.message.contains("element content"))
3853 .expect("expected element-content error");
3854 assert_eq!(issue.line, Some(4),
3855 "expected <bad> on line 4, got {issue:?}");
3856 }
3857
3858 #[test]
3859 fn issue_line_for_missing_required_element_points_at_parent() {
3860 let s = Schema::compile_str(&xsd_str(r#"
3861 <xs:element name="r">
3862 <xs:complexType>
3863 <xs:sequence>
3864 <xs:element name="x" type="xs:string"/>
3865 </xs:sequence>
3866 </xs:complexType>
3867 </xs:element>
3868 "#)).unwrap();
3869 let bad = format!("\n<r {}>\n</r>", instance_ns());
3871 let err = s.validate_str(&bad).unwrap_err();
3872 let issue = err.issues.iter()
3873 .find(|i| i.message.contains("missing required element"))
3874 .expect("expected missing-required-element error");
3875 assert_eq!(issue.line, Some(2),
3876 "expected <r> on line 2, got {issue:?}");
3877 }
3878
3879 #[test]
3880 fn issue_line_for_unexpected_element_points_at_the_element_not_parent() {
3881 let s = Schema::compile_str(&xsd_str(r#"
3882 <xs:element name="r">
3883 <xs:complexType>
3884 <xs:sequence>
3885 <xs:element name="ok" type="xs:string"/>
3886 </xs:sequence>
3887 </xs:complexType>
3888 </xs:element>
3889 "#)).unwrap();
3890 let bad = format!("<r {}>\n <bad/>\n</r>", instance_ns());
3893 let err = s.validate_str(&bad).unwrap_err();
3894 let issue = err.issues.iter()
3895 .find(|i| i.message.contains("unexpected element"))
3896 .expect("expected unexpected-element error");
3897 assert_eq!(issue.line, Some(2),
3898 "expected <bad> on line 2, got {issue:?}");
3899 }
3900
3901 #[test]
3902 fn issue_line_for_missing_required_attribute_points_at_element() {
3903 let s = Schema::compile_str(&xsd_str(r#"
3904 <xs:element name="r">
3905 <xs:complexType>
3906 <xs:attribute name="must" type="xs:string" use="required"/>
3907 </xs:complexType>
3908 </xs:element>
3909 "#)).unwrap();
3910 let bad = format!("\n\n<r {}/>", instance_ns());
3911 let err = s.validate_str(&bad).unwrap_err();
3912 let issue = err.issues.iter()
3913 .find(|i| i.message.contains("missing required attribute"))
3914 .expect("expected missing-required-attribute error");
3915 assert_eq!(issue.line, Some(3),
3916 "expected <r> on line 3, got {issue:?}");
3917 }
3918
3919 #[test]
3922 fn xsi_nil_true_with_empty_content_validates() {
3923 let s = Schema::compile_str(&xsd_str(r#"
3924 <xs:element name="opt" type="xs:int" nillable="true"/>
3925 "#)).unwrap();
3926 let xsi = "http://www.w3.org/2001/XMLSchema-instance";
3927 s.validate_str(&format!(r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true"/>"#,
3928 ns = instance_ns())).unwrap();
3929 s.validate_str(&format!(r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true"> </opt>"#,
3931 ns = instance_ns())).unwrap();
3932 }
3933
3934 #[test]
3935 fn xsi_nil_true_with_non_empty_content_fails() {
3936 let s = Schema::compile_str(&xsd_str(r#"
3937 <xs:element name="opt" type="xs:int" nillable="true"/>
3938 "#)).unwrap();
3939 let xsi = "http://www.w3.org/2001/XMLSchema-instance";
3940 let err = s.validate_str(&format!(
3941 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true">42</opt>"#,
3942 ns = instance_ns())).unwrap_err();
3943 assert!(err.issues.iter().any(|i|
3944 matches!(i.kind, ValidationKind::NillableViolation)
3945 ), "expected NillableViolation, got {:?}", err.issues);
3946 }
3947
3948 #[test]
3949 fn xsi_nil_on_non_nillable_element_fails() {
3950 let s = Schema::compile_str(&xsd_str(r#"
3951 <xs:element name="opt" type="xs:int"/>
3952 "#)).unwrap();
3953 let xsi = "http://www.w3.org/2001/XMLSchema-instance";
3954 let err = s.validate_str(&format!(
3955 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true"/>"#,
3956 ns = instance_ns())).unwrap_err();
3957 assert!(err.issues.iter().any(|i|
3958 matches!(i.kind, ValidationKind::NillableViolation)
3959 && i.message.contains("non-nillable")
3960 ), "expected non-nillable rejection, got {:?}", err.issues);
3961 }
3962
3963 #[test]
3964 fn xsi_nil_with_required_attribute_still_validates_attribute() {
3965 let s = Schema::compile_str(&xsd_str(r#"
3968 <xs:element name="opt" nillable="true">
3969 <xs:complexType>
3970 <xs:attribute name="id" type="xs:string" use="required"/>
3971 </xs:complexType>
3972 </xs:element>
3973 "#)).unwrap();
3974 let xsi = "http://www.w3.org/2001/XMLSchema-instance";
3975 s.validate_str(&format!(
3977 r#"<opt id="x" {ns} xmlns:xsi="{xsi}" xsi:nil="true"/>"#,
3978 ns = instance_ns())).unwrap();
3979 let err = s.validate_str(&format!(
3981 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true"/>"#,
3982 ns = instance_ns())).unwrap_err();
3983 assert!(err.issues.iter().any(|i|
3984 matches!(i.kind, ValidationKind::MissingRequiredAttribute)
3985 ), "expected MissingRequiredAttribute even under xsi:nil, got {:?}", err.issues);
3986 }
3987
3988 #[test]
3989 fn xsi_nil_skips_required_children_check() {
3990 let s = Schema::compile_str(&xsd_str(r#"
3994 <xs:element name="opt" nillable="true">
3995 <xs:complexType>
3996 <xs:sequence>
3997 <xs:element name="req" type="xs:string"/>
3998 </xs:sequence>
3999 </xs:complexType>
4000 </xs:element>
4001 "#)).unwrap();
4002 let xsi = "http://www.w3.org/2001/XMLSchema-instance";
4003 let err = s.validate_str(&format!(r#"<opt {ns}/>"#, ns = instance_ns())).unwrap_err();
4005 assert!(err.issues.iter().any(|i|
4006 matches!(i.kind, ValidationKind::MissingRequiredElement)
4007 ));
4008 s.validate_str(&format!(
4010 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true"/>"#,
4011 ns = instance_ns())).unwrap();
4012 }
4013
4014 #[test]
4015 fn xsi_nil_overrides_fixed_value_check() {
4016 let s = Schema::compile_str(&xsd_str(r#"
4021 <xs:element name="opt" type="xs:string" nillable="true" fixed="ABC"/>
4022 "#)).unwrap();
4023 let xsi = "http://www.w3.org/2001/XMLSchema-instance";
4024 s.validate_str(&format!(
4026 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="true"/>"#,
4027 ns = instance_ns())).unwrap();
4028 s.validate_str(&format!(r#"<opt {ns}>ABC</opt>"#, ns = instance_ns())).unwrap();
4030 let err = s.validate_str(&format!(
4031 r#"<opt {ns}>XYZ</opt>"#, ns = instance_ns())).unwrap_err();
4032 assert!(err.issues.iter().any(|i| i.message.contains("fixed")),
4033 "expected fixed mismatch, got {:?}", err.issues);
4034 }
4035
4036 #[test]
4037 fn xsi_nil_false_validates_normally() {
4038 let s = Schema::compile_str(&xsd_str(r#"
4041 <xs:element name="opt" type="xs:int" nillable="true"/>
4042 "#)).unwrap();
4043 let xsi = "http://www.w3.org/2001/XMLSchema-instance";
4044 s.validate_str(&format!(
4045 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="false">42</opt>"#,
4046 ns = instance_ns())).unwrap();
4047 let err = s.validate_str(&format!(
4048 r#"<opt {ns} xmlns:xsi="{xsi}" xsi:nil="false">foo</opt>"#,
4049 ns = instance_ns())).unwrap_err();
4050 assert!(err.issues.iter().any(|i|
4051 matches!(i.kind, ValidationKind::TypeMismatch)
4052 ), "expected TypeMismatch for non-int content, got {:?}", err.issues);
4053 }
4054
4055 #[test]
4056 fn xsi_nil_with_xsi_type_uses_substituted_type() {
4057 let s = Schema::compile_str(&xsd_str(r#"
4060 <xs:complexType name="Base">
4061 <xs:sequence>
4062 <xs:element name="child" type="xs:string"/>
4063 </xs:sequence>
4064 </xs:complexType>
4065 <xs:complexType name="Derived">
4066 <xs:complexContent>
4067 <xs:extension base="Base">
4068 <xs:sequence>
4069 <xs:element name="extra" type="xs:string"/>
4070 </xs:sequence>
4071 </xs:extension>
4072 </xs:complexContent>
4073 </xs:complexType>
4074 <xs:element name="x" type="Base" nillable="true"/>
4075 "#)).unwrap();
4076 let xsi = "http://www.w3.org/2001/XMLSchema-instance";
4077 s.validate_str(&format!(
4078 r#"<x {ns} xmlns:xsi="{xsi}" xsi:type="Derived" xsi:nil="true"/>"#,
4079 ns = instance_ns())).unwrap();
4080 }
4081
4082 #[test]
4085 fn redefine_replaces_simple_type_in_included_schema() {
4086 let included = r#"<?xml version="1.0"?>
4092 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
4093 targetNamespace="urn:test"
4094 xmlns="urn:test">
4095 <xs:simpleType name="Code">
4096 <xs:restriction base="xs:string">
4097 <xs:length value="3"/>
4098 </xs:restriction>
4099 </xs:simpleType>
4100 </xs:schema>
4101 "#;
4102 let outer = r#"<?xml version="1.0"?>
4103 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
4104 targetNamespace="urn:test"
4105 xmlns="urn:test">
4106 <xs:redefine schemaLocation="included.xsd">
4107 <xs:simpleType name="Code">
4108 <xs:restriction base="Code">
4109 <xs:enumeration value="ABC"/>
4110 <xs:enumeration value="XYZ"/>
4111 </xs:restriction>
4112 </xs:simpleType>
4113 </xs:redefine>
4114 <xs:element name="c" type="Code"/>
4115 </xs:schema>
4116 "#;
4117 let resolver = super::super::resolver::InMemoryResolver::new()
4118 .with("included.xsd", included.as_bytes().to_vec());
4119 let schema = Schema::compile_with(outer, resolver).unwrap();
4120 schema.validate_str(&format!(r#"<c {}>ABC</c>"#, instance_ns())).unwrap();
4122 schema.validate_str(&format!(r#"<c {}>XYZ</c>"#, instance_ns())).unwrap();
4123 let err = schema.validate_str(&format!(r#"<c {}>DEF</c>"#, instance_ns())).unwrap_err();
4126 assert!(err.issues.iter().any(|i| i.message.contains("enumeration")),
4127 "expected enumeration failure, got {:?}", err.issues);
4128 let err = schema.validate_str(&format!(r#"<c {}>TOOLONG</c>"#, instance_ns())).unwrap_err();
4130 assert!(!err.issues.is_empty(),
4131 "expected validation failure for too-long input, got ok");
4132 }
4133
4134 #[test]
4135 fn redefine_complex_type_extension_adds_fields() {
4136 let included = r#"<?xml version="1.0"?>
4140 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
4141 targetNamespace="urn:test"
4142 xmlns="urn:test"
4143 elementFormDefault="qualified">
4144 <xs:complexType name="Address">
4145 <xs:sequence>
4146 <xs:element name="city" type="xs:string"/>
4147 </xs:sequence>
4148 </xs:complexType>
4149 <xs:element name="addr" type="Address"/>
4150 </xs:schema>
4151 "#;
4152 let outer = r#"<?xml version="1.0"?>
4153 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
4154 xmlns:t="urn:test"
4155 targetNamespace="urn:test"
4156 xmlns="urn:test"
4157 elementFormDefault="qualified">
4158 <xs:redefine schemaLocation="included.xsd">
4159 <xs:complexType name="Address">
4160 <xs:complexContent>
4161 <xs:extension base="t:Address">
4162 <xs:sequence>
4163 <xs:element name="country" type="xs:string"/>
4164 </xs:sequence>
4165 </xs:extension>
4166 </xs:complexContent>
4167 </xs:complexType>
4168 </xs:redefine>
4169 </xs:schema>
4170 "#;
4171 let resolver = super::super::resolver::InMemoryResolver::new()
4172 .with("included.xsd", included.as_bytes().to_vec());
4173 let schema = Schema::compile_with(outer, resolver).unwrap();
4174 schema.validate_str(&format!(
4176 r#"<addr {ns}><city>SF</city><country>US</country></addr>"#,
4177 ns = instance_ns(),
4178 )).unwrap();
4179 let err = schema.validate_str(&format!(
4181 r#"<addr {ns}><city>SF</city></addr>"#,
4182 ns = instance_ns(),
4183 )).unwrap_err();
4184 assert!(err.issues.iter().any(|i|
4185 matches!(i.kind, ValidationKind::MissingRequiredElement)
4186 && i.message.contains("country")
4187 ), "expected missing-country error, got {:?}", err.issues);
4188 }
4189
4190 #[test]
4191 fn redefine_with_no_body_acts_like_include() {
4192 let included = r#"<?xml version="1.0"?>
4195 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
4196 targetNamespace="urn:test"
4197 xmlns="urn:test">
4198 <xs:element name="msg" type="xs:string"/>
4199 </xs:schema>
4200 "#;
4201 let outer = r#"<?xml version="1.0"?>
4202 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
4203 targetNamespace="urn:test"
4204 xmlns="urn:test">
4205 <xs:redefine schemaLocation="included.xsd"/>
4206 </xs:schema>
4207 "#;
4208 let resolver = super::super::resolver::InMemoryResolver::new()
4209 .with("included.xsd", included.as_bytes().to_vec());
4210 let schema = Schema::compile_with(outer, resolver).unwrap();
4211 schema.validate_str(&format!(r#"<msg {}>hi</msg>"#, instance_ns())).unwrap();
4212 }
4213
4214 #[test]
4217 fn restriction_chain_composes_facets_top_down() {
4218 let s = Schema::compile_str(&xsd_str(r#"
4225 <xs:simpleType name="A">
4226 <xs:restriction base="xs:string">
4227 <xs:maxLength value="20"/>
4228 </xs:restriction>
4229 </xs:simpleType>
4230 <xs:simpleType name="B">
4231 <xs:restriction base="A">
4232 <xs:pattern value="[A-Z]+"/>
4233 </xs:restriction>
4234 </xs:simpleType>
4235 <xs:simpleType name="C">
4236 <xs:restriction base="B">
4237 <xs:enumeration value="FOO"/>
4238 <xs:enumeration value="BAR"/>
4239 </xs:restriction>
4240 </xs:simpleType>
4241 <xs:element name="v" type="C"/>
4242 "#)).unwrap();
4243 s.validate_str(&format!(r#"<v {}>FOO</v>"#, instance_ns())).unwrap();
4245 s.validate_str(&format!(r#"<v {}>BAR</v>"#, instance_ns())).unwrap();
4246 let err = s.validate_str(&format!(r#"<v {}>ABC</v>"#, instance_ns())).unwrap_err();
4248 assert!(err.issues.iter().any(|i| i.message.contains("enumeration")),
4249 "expected enumeration failure, got {:?}", err.issues);
4250 let err = s.validate_str(&format!(r#"<v {}>foo</v>"#, instance_ns())).unwrap_err();
4252 assert!(err.issues.iter().any(|i| i.message.contains("pattern")),
4253 "expected pattern failure, got {:?}", err.issues);
4254 }
4255
4256 #[test]
4257 fn restriction_preserves_list_variety_from_base() {
4258 let s = Schema::compile_str(&xsd_str(r#"
4261 <xs:simpleType name="IntList">
4262 <xs:list itemType="xs:int"/>
4263 </xs:simpleType>
4264 <xs:simpleType name="ThreeInts">
4265 <xs:restriction base="IntList">
4266 <xs:length value="3"/>
4267 </xs:restriction>
4268 </xs:simpleType>
4269 <xs:element name="nums" type="ThreeInts"/>
4270 "#)).unwrap();
4271 s.validate_str(&format!(r#"<nums {}>1 2 3</nums>"#, instance_ns())).unwrap();
4272 let err = s.validate_str(&format!(r#"<nums {}>1 2</nums>"#, instance_ns())).unwrap_err();
4273 assert!(err.issues.iter().any(|i| i.message.contains("2 item(s)")),
4274 "expected list-length error counting items, got {:?}", err.issues);
4275 let err = s.validate_str(&format!(r#"<v {}>1 foo 3</v>"#, instance_ns())).unwrap_err();
4277 assert!(!err.issues.is_empty(),
4278 "expected validation failure for non-int item, got ok");
4279 }
4280
4281 #[test]
4284 fn xsi_type_accepts_derived_complex_type() {
4285 let s = Schema::compile_str(&xsd_str(r#"
4290 <xs:complexType name="Address">
4291 <xs:sequence>
4292 <xs:element name="city" type="xs:string"/>
4293 </xs:sequence>
4294 </xs:complexType>
4295 <xs:complexType name="USAddress">
4296 <xs:complexContent>
4297 <xs:extension base="Address">
4298 <xs:sequence>
4299 <xs:element name="state" type="xs:string"/>
4300 </xs:sequence>
4301 </xs:extension>
4302 </xs:complexContent>
4303 </xs:complexType>
4304 <xs:element name="addr" type="Address"/>
4305 "#)).unwrap();
4306 let ok = format!(
4307 r#"<addr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4308 xsi:type="USAddress" {ns}>
4309 <city>SF</city>
4310 <state>CA</state>
4311 </addr>"#,
4312 ns = instance_ns(),
4313 );
4314 s.validate_str(&ok).unwrap();
4315 }
4316
4317 #[test]
4318 fn extension_three_level_chain_merges_all_levels() {
4319 let s = Schema::compile_str(&xsd_str(r#"
4322 <xs:complexType name="A">
4323 <xs:sequence>
4324 <xs:element name="a" type="xs:string"/>
4325 </xs:sequence>
4326 </xs:complexType>
4327 <xs:complexType name="B">
4328 <xs:complexContent>
4329 <xs:extension base="A">
4330 <xs:sequence>
4331 <xs:element name="b" type="xs:string"/>
4332 </xs:sequence>
4333 </xs:extension>
4334 </xs:complexContent>
4335 </xs:complexType>
4336 <xs:complexType name="C">
4337 <xs:complexContent>
4338 <xs:extension base="B">
4339 <xs:sequence>
4340 <xs:element name="c" type="xs:string"/>
4341 </xs:sequence>
4342 </xs:extension>
4343 </xs:complexContent>
4344 </xs:complexType>
4345 <xs:element name="root" type="C"/>
4346 "#)).unwrap();
4347 let ok = format!(
4348 r#"<root {}>
4349 <a>x</a><b>y</b><c>z</c>
4350 </root>"#,
4351 instance_ns(),
4352 );
4353 s.validate_str(&ok).unwrap();
4354 }
4355
4356 #[test]
4357 fn extension_merges_attributes() {
4358 let s = Schema::compile_str(&xsd_str(r#"
4360 <xs:complexType name="Tagged">
4361 <xs:attribute name="id" type="xs:string" use="required"/>
4362 </xs:complexType>
4363 <xs:complexType name="TaggedNamed">
4364 <xs:complexContent>
4365 <xs:extension base="Tagged">
4366 <xs:attribute name="name" type="xs:string" use="required"/>
4367 </xs:extension>
4368 </xs:complexContent>
4369 </xs:complexType>
4370 <xs:element name="item" type="TaggedNamed"/>
4371 "#)).unwrap();
4372 s.validate_str(&format!(r#"<item id="x" name="y" {}/>"#, instance_ns())).unwrap();
4373 let err = s.validate_str(&format!(r#"<item name="y" {}/>"#, instance_ns())).unwrap_err();
4375 assert!(err.issues.iter().any(|i| i.message.contains("missing required attribute")
4376 && i.message.contains("id")
4377 ), "expected missing-id error, got {:?}", err.issues);
4378 }
4379
4380 #[test]
4381 fn xsi_type_rejects_unrelated_complex_type() {
4382 let s = Schema::compile_str(&xsd_str(r#"
4385 <xs:complexType name="Address">
4386 <xs:sequence>
4387 <xs:element name="city" type="xs:string"/>
4388 </xs:sequence>
4389 </xs:complexType>
4390 <xs:complexType name="Person">
4391 <xs:sequence>
4392 <xs:element name="name" type="xs:string"/>
4393 </xs:sequence>
4394 </xs:complexType>
4395 <xs:element name="addr" type="Address"/>
4396 "#)).unwrap();
4397 let bad = format!(
4398 r#"<addr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4399 xsi:type="Person" {ns}>
4400 <name>alice</name>
4401 </addr>"#,
4402 ns = instance_ns(),
4403 );
4404 let err = s.validate_str(&bad).unwrap_err();
4405 assert!(err.issues.iter().any(|i|
4406 matches!(i.kind, ValidationKind::TypeMismatch)
4407 && i.message.contains("does not derive from")
4408 ), "expected derivation-failure error, got {:?}", err.issues);
4409 }
4410
4411 #[test]
4412 fn xsi_type_accepts_identity_no_op() {
4413 let s = Schema::compile_str(&xsd_str(r#"
4415 <xs:complexType name="Address">
4416 <xs:sequence>
4417 <xs:element name="city" type="xs:string"/>
4418 </xs:sequence>
4419 </xs:complexType>
4420 <xs:element name="addr" type="Address"/>
4421 "#)).unwrap();
4422 let ok = format!(
4423 r#"<addr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4424 xsi:type="Address" {ns}>
4425 <city>SF</city>
4426 </addr>"#,
4427 ns = instance_ns(),
4428 );
4429 s.validate_str(&ok).unwrap();
4430 }
4431
4432 #[test]
4433 fn xsi_type_blocked_by_element_block_extension() {
4434 let s = Schema::compile_str(&xsd_str(r#"
4437 <xs:complexType name="Address">
4438 <xs:sequence>
4439 <xs:element name="city" type="xs:string"/>
4440 </xs:sequence>
4441 </xs:complexType>
4442 <xs:complexType name="USAddress">
4443 <xs:complexContent>
4444 <xs:extension base="Address">
4445 <xs:sequence>
4446 <xs:element name="state" type="xs:string"/>
4447 </xs:sequence>
4448 </xs:extension>
4449 </xs:complexContent>
4450 </xs:complexType>
4451 <xs:element name="addr" type="Address" block="extension"/>
4452 "#)).unwrap();
4453 let bad = format!(
4454 r#"<addr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4455 xsi:type="USAddress" {ns}>
4456 <city>SF</city>
4457 <state>CA</state>
4458 </addr>"#,
4459 ns = instance_ns(),
4460 );
4461 let err = s.validate_str(&bad).unwrap_err();
4462 assert!(err.issues.iter().any(|i|
4463 matches!(i.kind, ValidationKind::TypeMismatch)
4464 && i.message.contains("blocked")
4465 ), "expected block= rejection, got {:?}", err.issues);
4466 }
4467
4468 #[test]
4469 fn xsi_type_blocked_by_base_type_final_extension() {
4470 let result = Schema::compile_str(&xsd_str(r#"
4478 <xs:complexType name="Address" final="extension">
4479 <xs:sequence>
4480 <xs:element name="city" type="xs:string"/>
4481 </xs:sequence>
4482 </xs:complexType>
4483 <xs:complexType name="USAddress">
4484 <xs:complexContent>
4485 <xs:extension base="Address">
4486 <xs:sequence>
4487 <xs:element name="state" type="xs:string"/>
4488 </xs:sequence>
4489 </xs:extension>
4490 </xs:complexContent>
4491 </xs:complexType>
4492 <xs:element name="addr" type="Address"/>
4493 "#));
4494 let err = result.expect_err("schema must not compile");
4495 assert!(
4496 err.message.contains("final") && err.message.contains("extension"),
4497 "expected diagnostic mentioning final/extension, got: {}",
4498 err.message,
4499 );
4500 }
4501
4502 #[test]
4503 fn xsi_type_two_level_chain_derivation_check_accepts() {
4504 let s = Schema::compile_str(&xsd_str(r#"
4509 <xs:complexType name="A">
4510 <xs:sequence>
4511 <xs:element name="a" type="xs:string" minOccurs="0"/>
4512 </xs:sequence>
4513 </xs:complexType>
4514 <xs:complexType name="B">
4515 <xs:complexContent>
4516 <xs:extension base="A">
4517 <xs:sequence>
4518 <xs:element name="b" type="xs:string" minOccurs="0"/>
4519 </xs:sequence>
4520 </xs:extension>
4521 </xs:complexContent>
4522 </xs:complexType>
4523 <xs:complexType name="C">
4524 <xs:complexContent>
4525 <xs:extension base="B">
4526 <xs:sequence>
4527 <xs:element name="c" type="xs:string" minOccurs="0"/>
4528 </xs:sequence>
4529 </xs:extension>
4530 </xs:complexContent>
4531 </xs:complexType>
4532 <xs:element name="root" type="A" nillable="true"/>
4533 "#)).unwrap();
4534 let ok = format!(
4537 r#"<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4538 xsi:type="C" xsi:nil="true" {ns}/>"#,
4539 ns = instance_ns(),
4540 );
4541 s.validate_str(&ok).unwrap();
4542 }
4543
4544 #[test]
4547 fn xs_list_of_int_validates_each_item() {
4548 let s = Schema::compile_str(&xsd_str(r#"
4549 <xs:simpleType name="IntList">
4550 <xs:list itemType="xs:int"/>
4551 </xs:simpleType>
4552 <xs:element name="nums" type="IntList"/>
4553 "#)).unwrap();
4554 s.validate_str(&format!(r#"<nums {}>1 2 3</nums>"#, instance_ns())).unwrap();
4555 s.validate_str(&format!(r#"<nums {}></nums>"#, instance_ns())).unwrap();
4556 let err = s.validate_str(&format!(r#"<nums {}>1 foo 3</nums>"#, instance_ns())).unwrap_err();
4557 assert!(!err.issues.is_empty(),
4558 "expected at least one issue for 'foo' as int, got {:?}", err.issues);
4559 }
4560
4561 #[test]
4562 fn xs_list_length_facet_counts_items_not_chars() {
4563 let s = Schema::compile_str(&xsd_str(r#"
4567 <xs:simpleType name="IntList">
4568 <xs:list itemType="xs:int"/>
4569 </xs:simpleType>
4570 <xs:simpleType name="ThreeInts">
4571 <xs:restriction base="IntList">
4572 <xs:length value="3"/>
4573 </xs:restriction>
4574 </xs:simpleType>
4575 <xs:element name="nums" type="ThreeInts"/>
4576 "#)).unwrap();
4577 s.validate_str(&format!(r#"<nums {}>1 2 3</nums>"#, instance_ns())).unwrap();
4578 let too_few = s.validate_str(&format!(r#"<nums {}>1 2</nums>"#, instance_ns())).unwrap_err();
4579 assert!(too_few.issues.iter().any(|i| i.message.contains("length")),
4580 "expected length-facet error for 2 items, got {:?}", too_few.issues);
4581 let too_many = s.validate_str(&format!(r#"<nums {}>1 2 3 4</nums>"#, instance_ns())).unwrap_err();
4582 assert!(too_many.issues.iter().any(|i| i.message.contains("length")),
4583 "expected length-facet error for 4 items, got {:?}", too_many.issues);
4584 }
4585
4586 #[test]
4587 fn xs_union_accepts_any_member_type() {
4588 let s = Schema::compile_str(&xsd_str(r#"
4590 <xs:simpleType name="IntOrDate">
4591 <xs:union memberTypes="xs:int xs:date"/>
4592 </xs:simpleType>
4593 <xs:element name="val" type="IntOrDate"/>
4594 "#)).unwrap();
4595 s.validate_str(&format!(r#"<val {}>42</val>"#, instance_ns())).unwrap();
4596 s.validate_str(&format!(r#"<val {}>2026-05-16</val>"#, instance_ns())).unwrap();
4597 let err = s.validate_str(&format!(r#"<val {}>nonsense</val>"#, instance_ns())).unwrap_err();
4598 assert!(!err.issues.is_empty(),
4599 "expected union-failure, got {:?}", err.issues);
4600 }
4601
4602 #[test]
4603 fn xs_list_facet_length_unit_test() {
4604 use super::super::types::{SimpleType, Variety};
4608 use super::super::facets::{Facet, FacetSet};
4609 let mut list_facets = FacetSet::default();
4610 list_facets.push(Facet::Length(3));
4611 let three_ints = SimpleType {
4612 name: Some("ThreeInts".into()),
4613 builtin: BuiltinType::String,
4614 facets: list_facets,
4615 whitespace: super::super::whitespace::WhitespaceMode::Collapse,
4616 variety: Variety::List {
4617 item_type: Arc::new(SimpleType::of_builtin(BuiltinType::Int)),
4618 },
4619 final_: super::super::schema::BlockSet::default(),
4620 assertions: Vec::new(),
4621 };
4622 three_ints.validate("1 2 3").unwrap();
4623 let err = three_ints.validate("1 2").unwrap_err();
4624 assert!(err.message.contains("length") && err.message.contains("2 item(s)"),
4625 "expected list length error mentioning item count, got {}", err.message);
4626 let err = three_ints.validate("1 2 3 4").unwrap_err();
4627 assert!(err.message.contains("length") && err.message.contains("4 item(s)"),
4628 "expected list length error mentioning item count, got {}", err.message);
4629 let err = three_ints.validate("1 foo 3").unwrap_err();
4631 assert!(err.message.contains("list item #2"),
4632 "expected list-item error citing position, got {}", err.message);
4633 }
4634
4635 #[test]
4636 fn xs_union_with_nested_simpletypes() {
4637 let s = Schema::compile_str(&xsd_str(r#"
4639 <xs:simpleType name="SmallOrBig">
4640 <xs:union>
4641 <xs:simpleType>
4642 <xs:restriction base="xs:int">
4643 <xs:maxInclusive value="10"/>
4644 </xs:restriction>
4645 </xs:simpleType>
4646 <xs:simpleType>
4647 <xs:restriction base="xs:int">
4648 <xs:minInclusive value="1000"/>
4649 </xs:restriction>
4650 </xs:simpleType>
4651 </xs:union>
4652 </xs:simpleType>
4653 <xs:element name="n" type="SmallOrBig"/>
4654 "#)).unwrap();
4655 s.validate_str(&format!(r#"<n {}>5</n>"#, instance_ns())).unwrap();
4656 s.validate_str(&format!(r#"<n {}>2000</n>"#, instance_ns())).unwrap();
4657 let err = s.validate_str(&format!(r#"<n {}>500</n>"#, instance_ns())).unwrap_err();
4658 assert!(!err.issues.is_empty(),
4659 "expected union-failure for 500 (between 10 and 1000), got {:?}", err.issues);
4660 }
4661
4662 #[test]
4663 fn issue_line_for_duplicate_key_points_at_constraint_declaring_element() {
4664 let s = Schema::compile_str(&xsd_str(r#"
4665 <xs:element name="users">
4666 <xs:complexType>
4667 <xs:sequence>
4668 <xs:element name="user" maxOccurs="unbounded">
4669 <xs:complexType>
4670 <xs:attribute name="id" type="xs:string" use="required"/>
4671 </xs:complexType>
4672 </xs:element>
4673 </xs:sequence>
4674 </xs:complexType>
4675 <xs:key name="userKey">
4676 <xs:selector xpath=".//user"/>
4677 <xs:field xpath="@id"/>
4678 </xs:key>
4679 </xs:element>
4680 "#)).unwrap();
4681 let bad = format!(
4683 "\n\n<users {}>\n <user id=\"A\"/>\n <user id=\"A\"/>\n</users>",
4684 instance_ns()
4685 );
4686 let err = s.validate_str(&bad).unwrap_err();
4687 let issue = err.issues.iter()
4688 .find(|i| matches!(i.kind, ValidationKind::KeyNotUnique))
4689 .expect("expected KeyNotUnique error");
4690 assert_eq!(issue.line, Some(3),
4691 "expected <users> (declaring element) on line 3, got {issue:?}");
4692 }
4693
4694 #[test]
4695 fn validate_doc_typed_records_governing_types() {
4696 let s = Schema::compile_str(&xsd_str(r#"
4697 <xs:element name="root">
4698 <xs:complexType>
4699 <xs:sequence>
4700 <xs:element name="count" type="xs:integer"/>
4701 <xs:element name="label" type="xs:string"/>
4702 </xs:sequence>
4703 </xs:complexType>
4704 </xs:element>
4705 "#)).unwrap();
4706 let mut opts = crate::ParseOptions::default();
4707 opts.namespace_aware = true;
4708 let doc = crate::parse_str(
4709 &format!(r#"<root {}><count>3</count><label>hi</label></root>"#, instance_ns()),
4710 &opts,
4711 ).unwrap();
4712 let (res, psvi) = s.validate_doc_typed(&doc);
4713 assert!(res.is_ok(), "expected valid doc, got {res:?}");
4714 assert!(!psvi.is_empty(), "expected recorded type annotations");
4715
4716 let root = doc.root();
4719 assert!(psvi.governing_type(root).is_some(), "root should be typed");
4720 for child in root.children().filter(|n|
4721 matches!(n.kind, sup_xml_tree::dom::NodeKind::Element))
4722 {
4723 let ty = psvi.governing_type(child)
4724 .unwrap_or_else(|| panic!("{} should be typed", child.name()));
4725 let TypeRef::Simple(st) = ty else { panic!("expected simple type") };
4726 match child.name() {
4727 "count" => assert_eq!(st.builtin, BuiltinType::Integer),
4728 "label" => assert_eq!(st.builtin, BuiltinType::String),
4729 other => panic!("unexpected child {other}"),
4730 }
4731 }
4732 }
4733}