1use std::io::BufRead;
8
9use bumpalo::Bump;
10
11use crate::namespace::table::XML_NAMESPACE;
12use crate::parser::location::SourceSpan;
13use crate::schema::SchemaSet;
14use crate::validation::errors::ValidationError;
15use crate::validation::info::{SchemaInfo, ValidationFlags};
16use crate::validation::quick_xml_driver::{
17 drive_quick_xml_with, AttributeView, DriveWithError, ElementStartView, EndElementInfo,
18 EndOfAttributesView, TextKind, ValidationEventHandler,
19};
20use crate::validation::validator::{ValidationSink, ValidationWarning};
21use crate::validation::SchemaValidator;
22
23use super::buffer::BufferDocument;
24use super::builder::BufferDocumentBuilder;
25use super::error::BufferDocumentError;
26use super::type_remap::NodeSchemaBinding;
27use super::BufferDocumentOptions;
28
29pub struct SilentValidationSink;
36
37impl ValidationSink for SilentValidationSink {
38 fn on_error(&mut self, _error: ValidationError) {}
39 fn on_warning(&mut self, _warning: ValidationWarning) {}
40}
41
42struct TypedBuilderHandler<'b, 'a> {
52 builder: &'b mut BufferDocumentBuilder<'a>,
53 track: bool,
54 pending_start_offset: Option<usize>,
55 elem_ref_stack: Vec<u32>,
58 pending_spans: Vec<(u32, usize)>,
61 current_attr_ref: Option<u32>,
64 #[cfg(feature = "xsd11")]
67 deferred_attr_refs: Vec<u32>,
68 element_decl_stack: Vec<Option<crate::ids::ElementKey>>,
71}
72
73impl<'b, 'a> TypedBuilderHandler<'b, 'a> {
74 fn new(builder: &'b mut BufferDocumentBuilder<'a>) -> Self {
75 let track = builder.track_source_locations();
76 Self {
77 builder,
78 track,
79 pending_start_offset: None,
80 elem_ref_stack: Vec::new(),
81 pending_spans: Vec::new(),
82 current_attr_ref: None,
83 #[cfg(feature = "xsd11")]
84 deferred_attr_refs: Vec::new(),
85 element_decl_stack: Vec::new(),
86 }
87 }
88}
89
90impl<'b, 'a> ValidationEventHandler for TypedBuilderHandler<'b, 'a> {
91 type Error = BufferDocumentError;
92
93 fn on_element_start_offset(&mut self, byte_pos: usize) -> Result<(), Self::Error> {
94 if self.track {
95 self.pending_start_offset = Some(byte_pos);
96 }
97 Ok(())
98 }
99
100 fn before_element(&mut self, view: ElementStartView<'_>) -> Result<(), Self::Error> {
101 let elem_ref = self.builder.start_element(
102 view.local_name,
103 view.namespace_uri,
104 view.prefix,
105 view.namespace_decls,
106 )?;
107 self.elem_ref_stack.push(elem_ref);
108 if self.track {
109 if let Some(start) = self.pending_start_offset.take() {
110 self.pending_spans.push((elem_ref, start));
111 }
112 }
113 Ok(())
114 }
115
116 fn after_element(
117 &mut self,
118 _view: ElementStartView<'_>,
119 info: &SchemaInfo,
120 ) -> Result<(), Self::Error> {
121 let elem_ref = *self
122 .elem_ref_stack
123 .last()
124 .expect("after_element with empty stack");
125 self.element_decl_stack.push(info.element_decl);
126 if let Some(tk) = info.schema_type {
127 let binding = NodeSchemaBinding {
128 type_key: tk,
129 element_decl: info.element_decl,
130 attribute_decl: None,
131 content_type: info.content_type,
132 };
133 self.builder.set_node_binding(elem_ref, binding)?;
134 }
135 if info.is_nil {
136 self.builder.set_nil(elem_ref);
137 }
138 Ok(())
139 }
140
141 fn before_attribute(&mut self, view: AttributeView<'_>) -> Result<(), Self::Error> {
142 let attr_ref = self.builder.attribute(
143 view.local_name,
144 view.namespace_uri,
145 view.prefix,
146 view.value,
147 )?;
148 self.current_attr_ref = Some(attr_ref);
149 Ok(())
150 }
151
152 fn after_attribute(
153 &mut self,
154 view: AttributeView<'_>,
155 info: &SchemaInfo,
156 ) -> Result<(), Self::Error> {
157 let attr_ref = self
158 .current_attr_ref
159 .take()
160 .expect("after_attribute without matching before_attribute");
161 if let Some(tk) = info.schema_type {
162 let binding = NodeSchemaBinding {
163 type_key: tk,
164 element_decl: None,
165 attribute_decl: info.attribute_decl,
166 content_type: None,
167 };
168 self.builder.set_node_binding(attr_ref, binding)?;
169 }
170 if view.local_name == "id" && view.namespace_uri == XML_NAMESPACE {
171 let owner_elem = *self
172 .elem_ref_stack
173 .last()
174 .expect("xml:id without an open element");
175 self.builder.register_xml_id(view.value, owner_elem)?;
176 }
177 #[cfg(feature = "xsd11")]
178 if info.deferred_by_cta {
179 self.deferred_attr_refs.push(attr_ref);
180 }
181 Ok(())
182 }
183
184 fn after_end_of_attributes(
185 &mut self,
186 view: EndOfAttributesView<'_>,
187 ) -> Result<(), Self::Error> {
188 self.builder.end_of_attributes();
189 let elem_ref = *self
190 .elem_ref_stack
191 .last()
192 .expect("after_end_of_attributes without an open element");
193 let element_decl = *self
194 .element_decl_stack
195 .last()
196 .expect("after_end_of_attributes without an open element");
197
198 if let Some(tk) = view.info.schema_type {
199 let binding = NodeSchemaBinding {
200 type_key: tk,
201 element_decl,
202 attribute_decl: None,
203 content_type: view.info.content_type,
204 };
205 self.builder.set_node_binding(elem_ref, binding)?;
206 }
207
208 #[cfg(feature = "xsd11")]
209 {
210 let deferred_refs = std::mem::take(&mut self.deferred_attr_refs);
211 if deferred_refs.len() != view.deferred_attribute_results.len() {
212 return Err(BufferDocumentError::InternalError(
213 "deferred attribute count mismatch".into(),
214 ));
215 }
216 for (attr_ref, attr_info) in deferred_refs
217 .iter()
218 .zip(view.deferred_attribute_results.iter())
219 {
220 if let Some(tk) = attr_info.schema_type {
221 let binding = NodeSchemaBinding {
222 type_key: tk,
223 element_decl: None,
224 attribute_decl: attr_info.attribute_decl,
225 content_type: None,
226 };
227 self.builder.set_node_binding(*attr_ref, binding)?;
228 }
229 }
230 }
231
232 Ok(())
233 }
234
235 fn after_end_element(
236 &mut self,
237 _info: &EndElementInfo,
238 _depth: usize,
239 ) -> Result<(), Self::Error> {
240 self.builder.end_element()?;
241 self.elem_ref_stack.pop();
242 self.element_decl_stack.pop();
243 Ok(())
244 }
245
246 fn on_element_end_offset(&mut self, byte_pos: usize) -> Result<(), Self::Error> {
247 if self.track {
248 if let Some((elem_ref, start)) = self.pending_spans.pop() {
249 self.builder
250 .set_source_span(elem_ref, SourceSpan::new(start, byte_pos));
251 }
252 }
253 Ok(())
254 }
255
256 fn on_text(&mut self, _kind: TextKind, text: &str) -> Result<(), Self::Error> {
257 if !self.elem_ref_stack.is_empty() {
258 self.builder.text(text);
259 }
260 Ok(())
261 }
262
263 fn on_comment(&mut self, text: &str) -> Result<(), Self::Error> {
264 self.builder.comment(text)?;
265 Ok(())
266 }
267
268 fn on_processing_instruction(
269 &mut self,
270 target: &str,
271 data: &str,
272 ) -> Result<(), Self::Error> {
273 self.builder.processing_instruction(target, data)?;
274 Ok(())
275 }
276}
277
278pub fn build_typed_document<'a, R: BufRead>(
290 reader: R,
291 arena: &'a Bump,
292 schema_set: &'a SchemaSet,
293 options: BufferDocumentOptions,
294) -> Result<BufferDocument<'a>, BufferDocumentError> {
295 let mut builder =
296 BufferDocumentBuilder::new(arena, &schema_set.name_table, Some(schema_set), options)?;
297
298 let validator = SchemaValidator::new(schema_set, ValidationFlags::default());
299 let mut runtime = validator.start_run(SilentValidationSink);
300
301 {
302 let mut handler = TypedBuilderHandler::new(&mut builder);
303 drive_quick_xml_with(reader, &mut runtime, schema_set, &mut handler).map_err(|e| {
304 match e {
305 DriveWithError::Parse(e) => BufferDocumentError::Parse(e),
306 DriveWithError::Utf8(e) => BufferDocumentError::Utf8(e),
307 DriveWithError::UnboundPrefix(p) => BufferDocumentError::UnboundPrefix(p),
308 DriveWithError::UnexpectedEof { depth } => {
309 BufferDocumentError::InternalError(format!(
310 "unexpected EOF: {} element(s) still open",
311 depth
312 ))
313 }
314 DriveWithError::Hook(e) => e,
315 }
316 })?;
317 }
318
319 let _ = runtime.end_validation();
320 builder.finalize()
321}
322
323#[cfg(test)]
326mod tests {
327 use super::*;
328 use crate::ids::TypeKey;
329 use crate::navigator::{DomNavigator, TypedValue};
330 use crate::pipeline::load_and_process_schema;
331 use crate::validation::info::ContentType;
332
333 fn load_schema(xsd: &str) -> SchemaSet {
334 let mut schema_set = SchemaSet::xsd11();
335 load_and_process_schema(xsd.as_bytes(), "test.xsd", &mut schema_set, None)
336 .expect("failed to load schema");
337 schema_set
338 }
339
340 fn build_doc<'a>(xml: &str, arena: &'a Bump, schema_set: &'a SchemaSet) -> BufferDocument<'a> {
341 build_typed_document(
342 xml.as_bytes(),
343 arena,
344 schema_set,
345 BufferDocumentOptions::default(),
346 )
347 .expect("failed to build typed document")
348 }
349
350 #[test]
353 fn typed_document_has_schema_bindings() {
354 let schema_set = load_schema(
355 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
356 <xs:element name="root" type="xs:string"/>
357 </xs:schema>"#,
358 );
359 let arena = Bump::new();
360 let doc = build_doc("<root>hello</root>", &arena, &schema_set);
361
362 let mut nav = doc.create_navigator();
363 assert!(nav.move_to_first_child()); assert!(nav.element_type_key().is_some());
365 assert!(matches!(nav.element_type_key(), Some(TypeKey::Simple(_))));
366 }
367
368 #[test]
371 fn typed_value_integer_attribute() {
372 let schema_set = load_schema(
373 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
374 <xs:element name="root">
375 <xs:complexType>
376 <xs:attribute name="count" type="xs:integer"/>
377 </xs:complexType>
378 </xs:element>
379 </xs:schema>"#,
380 );
381 let arena = Bump::new();
382 let doc = build_doc(r#"<root count="42"/>"#, &arena, &schema_set);
383
384 let mut nav = doc.create_navigator();
385 assert!(nav.move_to_first_child()); assert!(nav.move_to_first_attribute());
387 assert!(nav.element_type_key().is_some());
388 let tv = nav.typed_value();
389 assert!(
390 matches!(tv, TypedValue::Value(_)),
391 "attribute should have typed value"
392 );
393 }
394
395 #[test]
398 fn typed_value_text_only_element() {
399 let schema_set = load_schema(
400 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
401 <xs:element name="root" type="xs:integer"/>
402 </xs:schema>"#,
403 );
404 let arena = Bump::new();
405 let doc = build_doc("<root>123</root>", &arena, &schema_set);
406
407 let mut nav = doc.create_navigator();
408 assert!(nav.move_to_first_child()); let tv = nav.typed_value();
410 assert!(
411 matches!(tv, TypedValue::Value(_)),
412 "simple-typed element should have typed value"
413 );
414 }
415
416 #[test]
419 fn typed_value_absent_for_element_only() {
420 let schema_set = load_schema(
421 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
422 <xs:element name="root">
423 <xs:complexType>
424 <xs:sequence>
425 <xs:element name="child" type="xs:string"/>
426 </xs:sequence>
427 </xs:complexType>
428 </xs:element>
429 </xs:schema>"#,
430 );
431 let arena = Bump::new();
432 let doc = build_doc("<root><child>hello</child></root>", &arena, &schema_set);
433
434 let mut nav = doc.create_navigator();
435 assert!(nav.move_to_first_child()); assert!(nav.element_type_key().is_some());
437 assert_eq!(
438 nav.typed_value(),
439 TypedValue::Absent,
440 "ElementOnly complex type should produce Absent"
441 );
442 }
443
444 #[test]
447 fn typed_value_simple_content() {
448 let schema_set = load_schema(
449 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
450 <xs:element name="root">
451 <xs:complexType>
452 <xs:simpleContent>
453 <xs:extension base="xs:integer">
454 <xs:attribute name="unit" type="xs:string"/>
455 </xs:extension>
456 </xs:simpleContent>
457 </xs:complexType>
458 </xs:element>
459 </xs:schema>"#,
460 );
461 let arena = Bump::new();
462 let doc = build_doc(r#"<root unit="kg">42</root>"#, &arena, &schema_set);
463
464 let mut nav = doc.create_navigator();
465 assert!(nav.move_to_first_child()); assert!(matches!(nav.element_type_key(), Some(TypeKey::Complex(_))));
467 let binding = nav.schema_binding().unwrap();
468 assert_eq!(
469 binding.content_type,
470 Some(ContentType::TextOnly),
471 "simpleContent should have TextOnly content type"
472 );
473 let tv = nav.typed_value();
474 assert!(
475 matches!(tv, TypedValue::Value(_)),
476 "simpleContent element should have typed_value"
477 );
478 }
479
480 #[test]
483 fn untyped_document_no_bindings() {
484 let schema_set = load_schema(
485 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
486 <xs:element name="other" type="xs:string"/>
487 </xs:schema>"#,
488 );
489 let arena = Bump::new();
490 let doc = build_doc("<root>hello</root>", &arena, &schema_set);
492
493 let mut nav = doc.create_navigator();
494 assert!(nav.move_to_first_child()); assert_eq!(nav.typed_value(), TypedValue::Untyped);
497 }
498
499 #[test]
502 fn xsi_type_override() {
503 let schema_set = load_schema(
504 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
505 <xs:element name="root" type="xs:string"/>
506 </xs:schema>"#,
507 );
508 let arena = Bump::new();
509 let doc = build_doc(
510 r#"<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
511 xmlns:xs="http://www.w3.org/2001/XMLSchema"
512 xsi:type="xs:integer">42</root>"#,
513 &arena,
514 &schema_set,
515 );
516
517 let mut nav = doc.create_navigator();
518 assert!(nav.move_to_first_child()); assert!(nav.element_type_key().is_some());
520 let tv = nav.typed_value();
522 assert!(
523 matches!(tv, TypedValue::Value(_)),
524 "xsi:type override should produce typed value"
525 );
526 }
527
528 #[test]
531 fn xsi_nil_sets_flag() {
532 let schema_set = load_schema(
533 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
534 <xs:element name="root" type="xs:string" nillable="true"/>
535 </xs:schema>"#,
536 );
537 let arena = Bump::new();
538 let doc = build_doc(
539 r#"<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
540 xsi:nil="true"/>"#,
541 &arena,
542 &schema_set,
543 );
544
545 let mut nav = doc.create_navigator();
546 assert!(nav.move_to_first_child()); assert_eq!(
548 nav.typed_value(),
549 TypedValue::Nilled,
550 "nil element should return Nilled"
551 );
552 }
553
554 #[test]
557 fn name_table_sharing() {
558 let schema_set = load_schema(
559 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
560 <xs:element name="root" type="xs:string"/>
561 </xs:schema>"#,
562 );
563 let arena = Bump::new();
564 let doc = build_doc("<root>hello</root>", &arena, &schema_set);
565
566 assert!(std::ptr::eq(doc.names(), &schema_set.name_table));
568 }
569
570 #[test]
573 fn element_type_key_simple_and_complex() {
574 let schema_set = load_schema(
575 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
576 <xs:element name="root">
577 <xs:complexType>
578 <xs:sequence>
579 <xs:element name="val" type="xs:integer"/>
580 </xs:sequence>
581 </xs:complexType>
582 </xs:element>
583 </xs:schema>"#,
584 );
585 let arena = Bump::new();
586 let doc = build_doc("<root><val>10</val></root>", &arena, &schema_set);
587
588 let mut nav = doc.create_navigator();
589 assert!(nav.move_to_first_child()); assert!(
591 matches!(nav.element_type_key(), Some(TypeKey::Complex(_))),
592 "root should have Complex type key"
593 );
594
595 assert!(nav.move_to_first_child());
597 assert!(
598 matches!(nav.element_type_key(), Some(TypeKey::Simple(_))),
599 "val should have Simple type key"
600 );
601 }
602
603 #[test]
606 fn default_value_on_empty_element() {
607 let schema_set = load_schema(
608 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
609 <xs:element name="root" type="xs:integer" default="99"/>
610 </xs:schema>"#,
611 );
612 let arena = Bump::new();
613 let doc = build_doc("<root/>", &arena, &schema_set);
615
616 let mut nav = doc.create_navigator();
617 assert!(nav.move_to_first_child()); let tv = nav.typed_value();
619 assert!(
620 matches!(tv, TypedValue::Value(_)),
621 "empty element with default should produce typed value"
622 );
623 }
624
625 #[test]
628 fn fixed_value_on_empty_element() {
629 let schema_set = load_schema(
630 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
631 <xs:element name="root" type="xs:integer" fixed="42"/>
632 </xs:schema>"#,
633 );
634 let arena = Bump::new();
635 let doc = build_doc("<root/>", &arena, &schema_set);
637
638 let mut nav = doc.create_navigator();
639 assert!(nav.move_to_first_child()); let tv = nav.typed_value();
641 assert!(
642 matches!(tv, TypedValue::Value(_)),
643 "empty element with fixed value should produce typed value, got: {:?}",
644 tv
645 );
646 }
647
648 #[test]
651 fn source_span_tracking() {
652 let schema_set = load_schema(
653 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
654 <xs:element name="root" type="xs:string"/>
655 </xs:schema>"#,
656 );
657 let arena = Bump::new();
658 let doc = build_typed_document(
659 "<root>hello</root>".as_bytes(),
660 &arena,
661 &schema_set,
662 BufferDocumentOptions::full(), )
664 .unwrap();
665
666 assert!(
668 !doc.source_spans.is_empty(),
669 "source spans should be recorded with full() options"
670 );
671 }
672
673 #[test]
676 fn comments_and_pis_preserved() {
677 use crate::document::node::NodeType;
678
679 let schema_set = load_schema(
680 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
681 <xs:element name="root">
682 <xs:complexType>
683 <xs:sequence>
684 <xs:element name="child" type="xs:string"/>
685 </xs:sequence>
686 </xs:complexType>
687 </xs:element>
688 </xs:schema>"#,
689 );
690 let arena = Bump::new();
691 let xml = "<root><!-- before --><?p1 d1?><child>hi</child><!-- after --></root>";
692 let doc = build_typed_document(
693 xml.as_bytes(),
694 &arena,
695 &schema_set,
696 BufferDocumentOptions::default(),
697 )
698 .unwrap();
699
700 let mut comment_count = 0usize;
701 let mut pi_count = 0usize;
702 for i in 0..doc.nodes.len() {
703 match doc.nodes.get(i).node_type() {
704 NodeType::Comment => comment_count += 1,
705 NodeType::ProcessingInstruction => pi_count += 1,
706 _ => {}
707 }
708 }
709 assert_eq!(comment_count, 2, "two comments should be preserved");
710 assert_eq!(pi_count, 1, "one PI should be preserved");
711 }
712
713 #[test]
716 fn fragment_set_node_binding_typed_value() {
717 let schema_set = load_schema(
718 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
719 <xs:element name="root" type="xs:integer"/>
720 </xs:schema>"#,
721 );
722 let arena = Bump::new();
723 let doc = build_typed_document(
724 "<root>42</root>".as_bytes(),
725 &arena,
726 &schema_set,
727 BufferDocumentOptions::fragment(),
728 )
729 .unwrap();
730
731 let mut nav = doc.create_navigator();
732 assert!(nav.move_to_first_child()); assert!(nav.element_type_key().is_some());
734 let tv = nav.typed_value();
735 assert!(
736 matches!(tv, TypedValue::Value(_)),
737 "fragment typed_value should resolve, got: {:?}",
738 tv
739 );
740 }
741
742 #[test]
743 fn fragment_schema_set_propagation() {
744 let schema_set = load_schema(
745 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
746 <xs:element name="root" type="xs:string"/>
747 </xs:schema>"#,
748 );
749 let arena = Bump::new();
750 let doc = build_typed_document(
751 "<root>hello</root>".as_bytes(),
752 &arena,
753 &schema_set,
754 BufferDocumentOptions::fragment(),
755 )
756 .unwrap();
757
758 assert!(
759 doc.schema_set().is_some(),
760 "fragment document should carry schema_set reference"
761 );
762 }
763
764 #[cfg(feature = "xsd11")]
767 mod cta_deferred_bindings {
768 use super::*;
769
770 const CTA_ATTR_SCHEMA: &str = r#"
773 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
774 <xs:complexType name="intType">
775 <xs:attribute name="kind" type="xs:string"/>
776 <xs:attribute name="val" type="xs:integer"/>
777 </xs:complexType>
778 <xs:complexType name="strType">
779 <xs:attribute name="kind" type="xs:string"/>
780 <xs:attribute name="val" type="xs:string"/>
781 </xs:complexType>
782 <xs:element name="data">
783 <xs:complexType>
784 <xs:attribute name="kind" type="xs:string"/>
785 <xs:attribute name="val" type="xs:string"/>
786 </xs:complexType>
787 <xs:alternative test="@kind='int'" type="intType"/>
788 <xs:alternative test="@kind='str'" type="strType"/>
789 </xs:element>
790 </xs:schema>"#;
791
792 #[test]
793 fn cta_selected_type_binds_attribute() {
794 let schema_set = load_schema(CTA_ATTR_SCHEMA);
795 let arena = Bump::new();
796 let doc = build_doc(r#"<data kind="int" val="42"/>"#, &arena, &schema_set);
797
798 let mut nav = doc.create_navigator();
799 assert!(nav.move_to_first_child()); assert!(nav.element_type_key().is_some());
802
803 assert!(nav.move_to_first_attribute());
805 if nav.local_name() == "kind" {
808 assert!(nav.move_to_next_attribute());
809 }
810 assert_eq!(nav.local_name(), "val");
811 assert!(
812 nav.element_type_key().is_some(),
813 "deferred CTA attribute 'val' should have a type binding"
814 );
815 }
816
817 #[test]
818 fn cta_default_type_binds_attribute() {
819 let schema_set = load_schema(CTA_ATTR_SCHEMA);
820 let arena = Bump::new();
821 let doc = build_doc(r#"<data kind="str" val="hello"/>"#, &arena, &schema_set);
823
824 let mut nav = doc.create_navigator();
825 assert!(nav.move_to_first_child()); assert!(nav.element_type_key().is_some());
827
828 assert!(nav.move_to_first_attribute());
829 if nav.local_name() == "kind" {
830 assert!(nav.move_to_next_attribute());
831 }
832 assert_eq!(nav.local_name(), "val");
833 assert!(
834 nav.element_type_key().is_some(),
835 "deferred CTA attribute 'val' should have a type binding for strType"
836 );
837 }
838
839 #[test]
840 fn cta_multiple_deferred_attributes_ordering() {
841 let schema = r#"
843 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
844 <xs:complexType name="fullType">
845 <xs:attribute name="kind" type="xs:string"/>
846 <xs:attribute name="x" type="xs:integer"/>
847 <xs:attribute name="y" type="xs:integer"/>
848 </xs:complexType>
849 <xs:element name="point">
850 <xs:complexType>
851 <xs:attribute name="kind" type="xs:string"/>
852 <xs:attribute name="x" type="xs:string"/>
853 <xs:attribute name="y" type="xs:string"/>
854 </xs:complexType>
855 <xs:alternative test="@kind='full'" type="fullType"/>
856 </xs:element>
857 </xs:schema>"#;
858
859 let schema_set = load_schema(schema);
860 let arena = Bump::new();
861 let doc = build_doc(r#"<point kind="full" x="10" y="20"/>"#, &arena, &schema_set);
862
863 let mut nav = doc.create_navigator();
864 assert!(nav.move_to_first_child()); assert!(nav.move_to_first_attribute());
868 let mut bound_count = 0;
869 loop {
870 if nav.element_type_key().is_some() {
871 bound_count += 1;
872 }
873 if !nav.move_to_next_attribute() {
874 break;
875 }
876 }
877 assert!(
878 bound_count >= 3,
879 "all 3 attributes (kind, x, y) should have type bindings, got {}",
880 bound_count
881 );
882 }
883
884 #[test]
885 fn cta_nested_elements_no_cross_leakage() {
886 let schema = r#"
888 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
889 <xs:complexType name="outerAlt">
890 <xs:sequence>
891 <xs:element ref="inner"/>
892 </xs:sequence>
893 <xs:attribute name="kind" type="xs:string"/>
894 <xs:attribute name="outerAttr" type="xs:integer"/>
895 </xs:complexType>
896 <xs:complexType name="innerAlt">
897 <xs:attribute name="kind" type="xs:string"/>
898 <xs:attribute name="innerAttr" type="xs:integer"/>
899 </xs:complexType>
900 <xs:element name="inner">
901 <xs:complexType>
902 <xs:attribute name="kind" type="xs:string"/>
903 <xs:attribute name="innerAttr" type="xs:string"/>
904 </xs:complexType>
905 <xs:alternative test="@kind='alt'" type="innerAlt"/>
906 </xs:element>
907 <xs:element name="outer">
908 <xs:complexType>
909 <xs:sequence>
910 <xs:element ref="inner"/>
911 </xs:sequence>
912 <xs:attribute name="kind" type="xs:string"/>
913 <xs:attribute name="outerAttr" type="xs:string"/>
914 </xs:complexType>
915 <xs:alternative test="@kind='alt'" type="outerAlt"/>
916 </xs:element>
917 </xs:schema>"#;
918
919 let schema_set = load_schema(schema);
920 let arena = Bump::new();
921 let doc = build_doc(
922 r#"<outer kind="alt" outerAttr="99"><inner kind="alt" innerAttr="42"/></outer>"#,
923 &arena,
924 &schema_set,
925 );
926
927 let mut nav = doc.create_navigator();
928 assert!(nav.move_to_first_child()); assert!(
930 nav.element_type_key().is_some(),
931 "outer should have type binding"
932 );
933
934 assert!(nav.move_to_first_attribute());
936 let mut found_outer_attr = false;
937 loop {
938 if nav.local_name() == "outerAttr" {
939 assert!(
940 nav.element_type_key().is_some(),
941 "outerAttr should have type binding"
942 );
943 found_outer_attr = true;
944 }
945 if !nav.move_to_next_attribute() {
946 break;
947 }
948 }
949 assert!(found_outer_attr, "should find outerAttr");
950
951 nav.move_to_parent();
953 assert!(nav.move_to_first_child()); assert!(
955 nav.element_type_key().is_some(),
956 "inner should have type binding"
957 );
958
959 assert!(nav.move_to_first_attribute());
961 let mut found_inner_attr = false;
962 loop {
963 if nav.local_name() == "innerAttr" {
964 assert!(
965 nav.element_type_key().is_some(),
966 "innerAttr should have type binding"
967 );
968 found_inner_attr = true;
969 }
970 if !nav.move_to_next_attribute() {
971 break;
972 }
973 }
974 assert!(found_inner_attr, "should find innerAttr");
975 }
976
977 #[test]
978 fn cta_simple_type_selection_no_panic() {
979 let schema = r#"
983 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
984 <xs:element name="data">
985 <xs:complexType>
986 <xs:attribute name="kind" type="xs:string"/>
987 <xs:attribute name="val" type="xs:string"/>
988 </xs:complexType>
989 <xs:alternative test="@kind='simple'" type="xs:string"/>
990 </xs:element>
991 </xs:schema>"#;
992
993 let schema_set = load_schema(schema);
994 let arena = Bump::new();
995 let result = build_typed_document(
998 r#"<data kind="simple" val="hello"/>"#.as_bytes(),
999 &arena,
1000 &schema_set,
1001 BufferDocumentOptions::default(),
1002 );
1003 assert!(
1004 result.is_ok(),
1005 "CTA selecting simple type should not cause InternalError, got: {:?}",
1006 result.err()
1007 );
1008 }
1009 }
1010}