1use std::fmt;
9
10use super::XmlTypeCode;
11use crate::ids::{SimpleTypeKey, TypeKey};
12use crate::namespace::qname::QualifiedName;
13use crate::schema::model::DerivationSet;
14use crate::types::value::XmlValue;
15use crate::xpath::cast::type_matches;
16use crate::xpath::context::XPathContext;
17use crate::xpath::iterator::XmlItem;
18use crate::xpath::{DomNavigator, DomNodeType};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
22pub enum XmlTypeCardinality {
23 #[default]
25 One,
26 ZeroOrOne,
28 OneOrMore,
30 ZeroOrMore,
32 Empty,
34}
35
36impl XmlTypeCardinality {
37 pub fn allows_empty(&self) -> bool {
39 matches!(self, Self::ZeroOrOne | Self::ZeroOrMore | Self::Empty)
40 }
41
42 pub fn allows_many(&self) -> bool {
44 matches!(self, Self::OneOrMore | Self::ZeroOrMore)
45 }
46
47 pub fn matches_count(&self, count: usize) -> bool {
49 match self {
50 Self::One => count == 1,
51 Self::ZeroOrOne => count <= 1,
52 Self::OneOrMore => count >= 1,
53 Self::ZeroOrMore => true,
54 Self::Empty => count == 0,
55 }
56 }
57
58 pub fn symbol(&self) -> &'static str {
60 match self {
61 Self::One => "",
62 Self::ZeroOrOne => "?",
63 Self::OneOrMore => "+",
64 Self::ZeroOrMore => "*",
65 Self::Empty => "", }
67 }
68}
69
70impl fmt::Display for XmlTypeCardinality {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 write!(f, "{}", self.symbol())
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
78pub enum ItemType {
79 AnyItem,
81 AnyNode,
83 Document(Option<Box<ItemType>>),
85 Element(Option<NameTest>, Option<SimpleTypeKey>),
87 Attribute(Option<NameTest>, Option<SimpleTypeKey>),
89 SchemaElement(QualifiedName),
91 SchemaAttribute(QualifiedName),
93 Text,
95 Comment,
97 ProcessingInstruction(Option<String>),
99 NamespaceNode,
101 AtomicType(XmlTypeCode),
103 SchemaAtomicType(SimpleTypeKey),
105}
106
107impl ItemType {
108 pub fn type_code(&self) -> XmlTypeCode {
110 match self {
111 Self::AnyItem => XmlTypeCode::Item,
112 Self::AnyNode => XmlTypeCode::Node,
113 Self::Document(_) => XmlTypeCode::Document,
114 Self::Element(_, _) | Self::SchemaElement(_) => XmlTypeCode::Element,
115 Self::Attribute(_, _) | Self::SchemaAttribute(_) => XmlTypeCode::Attribute,
116 Self::Text => XmlTypeCode::Text,
117 Self::Comment => XmlTypeCode::Comment,
118 Self::ProcessingInstruction(_) => XmlTypeCode::ProcessingInstruction,
119 Self::NamespaceNode => XmlTypeCode::Namespace,
120 Self::AtomicType(code) => *code,
121 Self::SchemaAtomicType(_) => XmlTypeCode::AnyAtomicType,
122 }
123 }
124
125 pub fn is_node(&self) -> bool {
127 matches!(
128 self,
129 Self::AnyNode
130 | Self::Document(_)
131 | Self::Element(_, _)
132 | Self::Attribute(_, _)
133 | Self::SchemaElement(_)
134 | Self::SchemaAttribute(_)
135 | Self::Text
136 | Self::Comment
137 | Self::ProcessingInstruction(_)
138 | Self::NamespaceNode
139 )
140 }
141
142 pub fn is_atomic(&self) -> bool {
144 matches!(self, Self::AtomicType(_) | Self::SchemaAtomicType(_))
145 }
146
147 pub fn matches_item<N: DomNavigator>(&self, item: &XmlItem<N>, ctx: &XPathContext<'_>) -> bool {
152 match item {
153 XmlItem::Node(nav) => self.matches_node(nav, ctx),
154 XmlItem::Atomic(value) => self.matches_atomic(value, ctx),
155 }
156 }
157
158 fn matches_node<N: DomNavigator>(&self, nav: &N, ctx: &XPathContext<'_>) -> bool {
160 match self {
161 Self::AnyItem => true,
162 Self::AnyNode => true,
163 Self::Document(None) => nav.node_type() == DomNodeType::Root,
164 Self::Document(Some(inner)) => {
165 if nav.node_type() != DomNodeType::Root {
166 return false;
167 }
168 let mut cursor = nav.clone();
172 if !cursor.move_to_first_child() {
173 return false;
174 }
175
176 let mut element_count = 0;
177 let mut matching_element: Option<N> = None;
178
179 loop {
180 let node_type = cursor.node_type();
181 match node_type {
182 DomNodeType::Element => {
183 element_count += 1;
184 if element_count > 1 {
185 return false;
187 }
188 matching_element = Some(cursor.clone());
190 }
191 DomNodeType::Comment | DomNodeType::ProcessingInstruction => {
192 }
194 DomNodeType::Text
195 | DomNodeType::Whitespace
196 | DomNodeType::SignificantWhitespace => {
197 }
200 _ => {
201 }
203 }
204 if !cursor.move_to_next_sibling() {
205 break;
206 }
207 }
208
209 if element_count != 1 {
211 return false;
212 }
213
214 match matching_element {
216 Some(elem) => inner.matches_node(&elem, ctx),
217 None => false,
218 }
219 }
220 Self::Element(name_test, schema_type) => {
221 if nav.node_type() != DomNodeType::Element {
222 return false;
223 }
224 if let Some(test) = name_test {
225 if !Self::matches_name_test(test, nav, ctx) {
226 return false;
227 }
228 }
229 if let Some(expected) = schema_type {
230 if !Self::matches_schema_type(nav, *expected, ctx) {
231 return false;
232 }
233 }
234 true
235 }
236 Self::Attribute(name_test, schema_type) => {
237 if nav.node_type() != DomNodeType::Attribute {
238 return false;
239 }
240 if let Some(test) = name_test {
241 if !Self::matches_name_test(test, nav, ctx) {
242 return false;
243 }
244 }
245 if let Some(expected) = schema_type {
246 if !Self::matches_schema_type(nav, *expected, ctx) {
247 return false;
248 }
249 }
250 true
251 }
252 Self::SchemaElement(name) => {
253 if nav.node_type() != DomNodeType::Element {
254 return false;
255 }
256 if !Self::matches_qname(name, nav, ctx) {
257 return false;
258 }
259 Self::matches_schema_element_decl(nav, name, ctx)
260 }
261 Self::SchemaAttribute(name) => {
262 if nav.node_type() != DomNodeType::Attribute {
263 return false;
264 }
265 if !Self::matches_qname(name, nav, ctx) {
266 return false;
267 }
268 Self::matches_schema_attribute_decl(nav, name, ctx)
269 }
270 Self::Text => nav.node_type().is_text_like(),
271 Self::Comment => nav.node_type() == DomNodeType::Comment,
272 Self::ProcessingInstruction(target) => {
273 nav.node_type() == DomNodeType::ProcessingInstruction
274 && target.as_ref().is_none_or(|name| nav.local_name() == name)
275 }
276 Self::NamespaceNode => nav.node_type() == DomNodeType::Namespace,
277 Self::AtomicType(_) | Self::SchemaAtomicType(_) => false,
279 }
280 }
281
282 fn matches_atomic(&self, value: &XmlValue, ctx: &XPathContext<'_>) -> bool {
284 match self {
285 Self::AnyItem => true,
286 Self::AnyNode => false, Self::AtomicType(code) => type_matches(value.type_code, *code),
288 Self::SchemaAtomicType(key) => {
289 if let Some(value_key) = value.schema_type {
291 if let Some(schema_set) = ctx.schema_set {
292 return schema_set.is_type_derived_from(
293 TypeKey::Simple(value_key),
294 TypeKey::Simple(*key),
295 DerivationSet::empty(),
296 );
297 }
298 return value_key == *key;
300 }
301 false
303 }
304 Self::Document(_)
306 | Self::Element(_, _)
307 | Self::Attribute(_, _)
308 | Self::SchemaElement(_)
309 | Self::SchemaAttribute(_)
310 | Self::Text
311 | Self::Comment
312 | Self::ProcessingInstruction(_)
313 | Self::NamespaceNode => false,
314 }
315 }
316
317 fn matches_name_test<N: DomNavigator>(
319 test: &NameTest,
320 nav: &N,
321 ctx: &XPathContext<'_>,
322 ) -> bool {
323 match test {
324 NameTest::Wildcard => true,
325 NameTest::NamespaceWildcard(local_id) => {
326 match ctx.resolve_name(*local_id) {
328 Some(local) => nav.local_name() == local,
329 None => false,
330 }
331 }
332 NameTest::LocalWildcard(ns_id) => {
333 match ctx.resolve_name(*ns_id) {
335 Some(ns) => nav.namespace_uri() == ns,
336 None => false,
337 }
338 }
339 NameTest::QName(qname) => Self::matches_qname(qname, nav, ctx),
340 }
341 }
342
343 fn matches_qname<N: DomNavigator>(
345 qname: &QualifiedName,
346 nav: &N,
347 ctx: &XPathContext<'_>,
348 ) -> bool {
349 let local = match ctx.resolve_name(qname.local_name) {
350 Some(local) => local,
351 None => return false,
352 };
353 let ns = match qname.namespace_uri {
354 Some(id) => match ctx.resolve_name(id) {
355 Some(ns) => ns,
356 None => return false,
357 },
358 None => String::new(),
359 };
360 nav.local_name() == local && nav.namespace_uri() == ns
361 }
362
363 fn matches_schema_type<N: DomNavigator>(
365 nav: &N,
366 expected: SimpleTypeKey,
367 ctx: &XPathContext<'_>,
368 ) -> bool {
369 if let Some(actual) = nav.schema_type() {
370 if let Some(schema_set) = ctx.schema_set {
371 return schema_set.is_type_derived_from(
372 TypeKey::Simple(actual),
373 TypeKey::Simple(expected),
374 DerivationSet::empty(),
375 );
376 }
377 return actual == expected;
379 }
380 false
382 }
383
384 fn matches_schema_element_decl<N: DomNavigator>(
386 nav: &N,
387 name: &QualifiedName,
388 ctx: &XPathContext<'_>,
389 ) -> bool {
390 if let Some(schema_set) = ctx.schema_set {
391 let ns_id = name.namespace_uri;
392 let Some(elem_key) = schema_set.lookup_element(ns_id, name.local_name) else {
393 return false;
394 };
395 let Some(elem_data) = schema_set.arenas.elements.get(elem_key) else {
396 return false;
397 };
398 if let Some(expected_type) = elem_data.resolved_type {
399 let Some(actual_type) = nav.schema_type() else {
400 return false;
401 };
402 return schema_set.is_type_derived_from(
403 TypeKey::Simple(actual_type),
404 expected_type,
405 DerivationSet::empty(),
406 );
407 }
408 return true;
410 }
411 true
413 }
414
415 fn matches_schema_attribute_decl<N: DomNavigator>(
417 nav: &N,
418 name: &QualifiedName,
419 ctx: &XPathContext<'_>,
420 ) -> bool {
421 if let Some(schema_set) = ctx.schema_set {
422 let ns_id = name.namespace_uri;
423 let Some(attr_key) = schema_set.lookup_attribute(ns_id, name.local_name) else {
424 return false;
425 };
426 let Some(attr_data) = schema_set.arenas.attributes.get(attr_key) else {
427 return false;
428 };
429 if let Some(expected_type) = attr_data.resolved_type {
430 let Some(actual_type) = nav.schema_type() else {
431 return false;
432 };
433 return schema_set.is_type_derived_from(
434 TypeKey::Simple(actual_type),
435 expected_type,
436 DerivationSet::empty(),
437 );
438 }
439 return true;
441 }
442 true
444 }
445}
446
447#[derive(Debug, Clone, PartialEq, Eq)]
449pub enum NameTest {
450 Wildcard,
452 NamespaceWildcard(crate::ids::NameId),
455 LocalWildcard(crate::ids::NameId),
458 QName(QualifiedName),
460}
461
462impl NameTest {
463 pub fn is_wildcard(&self) -> bool {
465 matches!(self, Self::Wildcard)
466 }
467}
468
469#[derive(Debug, Clone, PartialEq, Eq)]
474pub struct SequenceType {
475 pub item_type: ItemType,
477 pub cardinality: XmlTypeCardinality,
479}
480
481impl SequenceType {
482 pub fn new(item_type: ItemType, cardinality: XmlTypeCardinality) -> Self {
484 Self {
485 item_type,
486 cardinality,
487 }
488 }
489
490 pub fn one(item_type: ItemType) -> Self {
492 Self::new(item_type, XmlTypeCardinality::One)
493 }
494
495 pub fn optional(item_type: ItemType) -> Self {
497 Self::new(item_type, XmlTypeCardinality::ZeroOrOne)
498 }
499
500 pub fn plus(item_type: ItemType) -> Self {
502 Self::new(item_type, XmlTypeCardinality::OneOrMore)
503 }
504
505 pub fn star(item_type: ItemType) -> Self {
507 Self::new(item_type, XmlTypeCardinality::ZeroOrMore)
508 }
509
510 pub fn empty() -> Self {
514 Self::new(ItemType::AnyItem, XmlTypeCardinality::Empty)
515 }
516
517 pub fn is_empty_sequence(&self) -> bool {
519 self.cardinality == XmlTypeCardinality::Empty
520 }
521
522 pub fn any() -> Self {
524 Self::star(ItemType::AnyItem)
525 }
526
527 pub fn item() -> Self {
529 Self::one(ItemType::AnyItem)
530 }
531
532 pub fn node() -> Self {
534 Self::one(ItemType::AnyNode)
535 }
536
537 pub fn nodes() -> Self {
539 Self::star(ItemType::AnyNode)
540 }
541
542 pub fn string() -> Self {
546 Self::one(ItemType::AtomicType(XmlTypeCode::String))
547 }
548
549 pub fn string_optional() -> Self {
551 Self::optional(ItemType::AtomicType(XmlTypeCode::String))
552 }
553
554 pub fn boolean() -> Self {
556 Self::one(ItemType::AtomicType(XmlTypeCode::Boolean))
557 }
558
559 pub fn integer() -> Self {
561 Self::one(ItemType::AtomicType(XmlTypeCode::Integer))
562 }
563
564 pub fn integer_optional() -> Self {
566 Self::optional(ItemType::AtomicType(XmlTypeCode::Integer))
567 }
568
569 pub fn decimal() -> Self {
571 Self::one(ItemType::AtomicType(XmlTypeCode::Decimal))
572 }
573
574 pub fn double() -> Self {
576 Self::one(ItemType::AtomicType(XmlTypeCode::Double))
577 }
578
579 pub fn double_optional() -> Self {
581 Self::optional(ItemType::AtomicType(XmlTypeCode::Double))
582 }
583
584 pub fn any_atomic() -> Self {
586 Self::one(ItemType::AtomicType(XmlTypeCode::AnyAtomicType))
587 }
588
589 pub fn any_atomic_optional() -> Self {
591 Self::optional(ItemType::AtomicType(XmlTypeCode::AnyAtomicType))
592 }
593
594 pub fn any_atomic_star() -> Self {
596 Self::star(ItemType::AtomicType(XmlTypeCode::AnyAtomicType))
597 }
598
599 pub fn datetime() -> Self {
601 Self::one(ItemType::AtomicType(XmlTypeCode::DateTime))
602 }
603
604 pub fn date() -> Self {
606 Self::one(ItemType::AtomicType(XmlTypeCode::Date))
607 }
608
609 pub fn time() -> Self {
611 Self::one(ItemType::AtomicType(XmlTypeCode::Time))
612 }
613
614 pub fn duration() -> Self {
616 Self::one(ItemType::AtomicType(XmlTypeCode::Duration))
617 }
618
619 pub fn qname() -> Self {
621 Self::one(ItemType::AtomicType(XmlTypeCode::QName))
622 }
623
624 pub fn any_uri() -> Self {
626 Self::one(ItemType::AtomicType(XmlTypeCode::AnyUri))
627 }
628
629 pub fn type_code(&self) -> XmlTypeCode {
633 self.item_type.type_code()
634 }
635
636 pub fn is_node(&self) -> bool {
638 self.item_type.is_node()
639 }
640
641 pub fn is_atomic(&self) -> bool {
643 self.item_type.is_atomic()
644 }
645
646 pub fn is_numeric(&self) -> bool {
648 self.type_code().is_numeric()
649 }
650
651 pub fn allows_empty(&self) -> bool {
653 self.cardinality.allows_empty()
654 }
655
656 pub fn matches_sequence<N: DomNavigator>(
660 &self,
661 items: &[XmlItem<N>],
662 ctx: &XPathContext<'_>,
663 ) -> bool {
664 if !self.cardinality.matches_count(items.len()) {
666 return false;
667 }
668 for item in items {
670 if !self.item_type.matches_item(item, ctx) {
671 return false;
672 }
673 }
674 true
675 }
676}
677
678impl Default for SequenceType {
679 fn default() -> Self {
680 Self::any()
681 }
682}
683
684impl fmt::Display for SequenceType {
685 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
686 if self.cardinality == XmlTypeCardinality::Empty {
688 return write!(f, "empty-sequence()");
689 }
690
691 match &self.item_type {
693 ItemType::AnyItem => write!(f, "item()")?,
694 ItemType::AnyNode => write!(f, "node()")?,
695 ItemType::Document(None) => write!(f, "document-node()")?,
696 ItemType::Document(Some(elem)) => write!(f, "document-node({:?})", elem)?,
697 ItemType::Element(None, None) => write!(f, "element()")?,
698 ItemType::Element(Some(name), None) => write!(f, "element({:?})", name)?,
699 ItemType::Element(name, Some(_type_key)) => {
700 write!(f, "element({:?}, ...)", name)?;
701 }
702 ItemType::Attribute(None, None) => write!(f, "attribute()")?,
703 ItemType::Attribute(Some(name), None) => write!(f, "attribute({:?})", name)?,
704 ItemType::Attribute(name, Some(_type_key)) => {
705 write!(f, "attribute({:?}, ...)", name)?;
706 }
707 ItemType::SchemaElement(qn) => write!(f, "schema-element({:?})", qn)?,
708 ItemType::SchemaAttribute(qn) => write!(f, "schema-attribute({:?})", qn)?,
709 ItemType::Text => write!(f, "text()")?,
710 ItemType::Comment => write!(f, "comment()")?,
711 ItemType::ProcessingInstruction(None) => write!(f, "processing-instruction()")?,
712 ItemType::ProcessingInstruction(Some(name)) => {
713 write!(f, "processing-instruction({})", name)?;
714 }
715 ItemType::NamespaceNode => write!(f, "namespace-node()")?,
716 ItemType::AtomicType(code) => {
717 if let Some(name) = code.local_name() {
718 write!(f, "xs:{}", name)?;
719 } else {
720 write!(f, "{:?}", code)?;
721 }
722 }
723 ItemType::SchemaAtomicType(_key) => write!(f, "schema-type(...)")?,
724 }
725
726 write!(f, "{}", self.cardinality)?;
728
729 Ok(())
730 }
731}
732
733#[cfg(feature = "xsd11")]
743pub fn resolve_list_item_schema_type(
744 list_type_key: SimpleTypeKey,
745 schema_set: &crate::schema::SchemaSet,
746) -> Option<SimpleTypeKey> {
747 let st_data = schema_set.arenas.simple_types.get(list_type_key)?;
748 st_data.resolved_item_type?.as_simple()
749}
750
751#[cfg(test)]
756mod tests {
757 use super::*;
758 use num_bigint::BigInt;
759
760 use crate::namespace::table::NameTable;
761 use crate::navigator::RoXmlNavigator;
762
763 #[test]
764 fn test_cardinality_matches_count() {
765 assert!(XmlTypeCardinality::One.matches_count(1));
766 assert!(!XmlTypeCardinality::One.matches_count(0));
767 assert!(!XmlTypeCardinality::One.matches_count(2));
768
769 assert!(XmlTypeCardinality::ZeroOrOne.matches_count(0));
770 assert!(XmlTypeCardinality::ZeroOrOne.matches_count(1));
771 assert!(!XmlTypeCardinality::ZeroOrOne.matches_count(2));
772
773 assert!(!XmlTypeCardinality::OneOrMore.matches_count(0));
774 assert!(XmlTypeCardinality::OneOrMore.matches_count(1));
775 assert!(XmlTypeCardinality::OneOrMore.matches_count(100));
776
777 assert!(XmlTypeCardinality::ZeroOrMore.matches_count(0));
778 assert!(XmlTypeCardinality::ZeroOrMore.matches_count(1));
779 assert!(XmlTypeCardinality::ZeroOrMore.matches_count(100));
780 }
781
782 #[test]
783 fn test_cardinality_symbols() {
784 assert_eq!(XmlTypeCardinality::One.symbol(), "");
785 assert_eq!(XmlTypeCardinality::ZeroOrOne.symbol(), "?");
786 assert_eq!(XmlTypeCardinality::OneOrMore.symbol(), "+");
787 assert_eq!(XmlTypeCardinality::ZeroOrMore.symbol(), "*");
788 }
789
790 #[test]
791 fn test_sequence_type_display() {
792 assert_eq!(SequenceType::item().to_string(), "item()");
793 assert_eq!(SequenceType::string().to_string(), "xs:string");
794 assert_eq!(SequenceType::string_optional().to_string(), "xs:string?");
795 assert_eq!(SequenceType::nodes().to_string(), "node()*");
796 assert_eq!(SequenceType::integer().to_string(), "xs:integer");
797 }
798
799 #[test]
800 fn test_sequence_type_is_node() {
801 assert!(SequenceType::node().is_node());
802 assert!(SequenceType::nodes().is_node());
803 assert!(!SequenceType::string().is_node());
804 assert!(!SequenceType::any_atomic().is_node());
805 }
806
807 #[test]
808 fn test_sequence_type_is_atomic() {
809 assert!(SequenceType::string().is_atomic());
810 assert!(SequenceType::integer().is_atomic());
811 assert!(SequenceType::any_atomic().is_atomic());
812 assert!(!SequenceType::node().is_atomic());
813 assert!(!SequenceType::item().is_atomic());
814 }
815
816 #[test]
817 fn test_sequence_type_is_numeric() {
818 assert!(SequenceType::decimal().is_numeric());
819 assert!(SequenceType::integer().is_numeric());
820 assert!(SequenceType::double().is_numeric());
821 assert!(!SequenceType::string().is_numeric());
822 assert!(!SequenceType::boolean().is_numeric());
823 }
824
825 #[test]
826 fn test_item_type_type_code() {
827 assert_eq!(ItemType::AnyItem.type_code(), XmlTypeCode::Item);
828 assert_eq!(ItemType::AnyNode.type_code(), XmlTypeCode::Node);
829 assert_eq!(ItemType::Text.type_code(), XmlTypeCode::Text);
830 assert_eq!(
831 ItemType::AtomicType(XmlTypeCode::String).type_code(),
832 XmlTypeCode::String
833 );
834 }
835
836 #[test]
841 fn test_cardinality_one() {
842 let seq_type = SequenceType::integer();
844 let table = NameTable::new();
845 let ctx = XPathContext::new(&table);
846
847 let one_item: Vec<XmlItem<RoXmlNavigator<'static>>> =
848 vec![XmlItem::Atomic(XmlValue::integer(BigInt::from(42)))];
849 let no_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![];
850 let two_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![
851 XmlItem::Atomic(XmlValue::integer(BigInt::from(1))),
852 XmlItem::Atomic(XmlValue::integer(BigInt::from(2))),
853 ];
854
855 assert!(seq_type.matches_sequence(&one_item, &ctx));
856 assert!(!seq_type.matches_sequence(&no_items, &ctx));
857 assert!(!seq_type.matches_sequence(&two_items, &ctx));
858 }
859
860 #[test]
861 fn test_cardinality_optional() {
862 let seq_type = SequenceType::integer_optional();
864 let table = NameTable::new();
865 let ctx = XPathContext::new(&table);
866
867 let one_item: Vec<XmlItem<RoXmlNavigator<'static>>> =
868 vec![XmlItem::Atomic(XmlValue::integer(BigInt::from(42)))];
869 let no_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![];
870 let two_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![
871 XmlItem::Atomic(XmlValue::integer(BigInt::from(1))),
872 XmlItem::Atomic(XmlValue::integer(BigInt::from(2))),
873 ];
874
875 assert!(seq_type.matches_sequence(&one_item, &ctx));
876 assert!(seq_type.matches_sequence(&no_items, &ctx));
877 assert!(!seq_type.matches_sequence(&two_items, &ctx));
878 }
879
880 #[test]
881 fn test_cardinality_star() {
882 let seq_type = SequenceType::star(ItemType::AtomicType(XmlTypeCode::Integer));
884 let table = NameTable::new();
885 let ctx = XPathContext::new(&table);
886
887 let no_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![];
888 let one_item: Vec<XmlItem<RoXmlNavigator<'static>>> =
889 vec![XmlItem::Atomic(XmlValue::integer(BigInt::from(42)))];
890 let three_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![
891 XmlItem::Atomic(XmlValue::integer(BigInt::from(1))),
892 XmlItem::Atomic(XmlValue::integer(BigInt::from(2))),
893 XmlItem::Atomic(XmlValue::integer(BigInt::from(3))),
894 ];
895
896 assert!(seq_type.matches_sequence(&no_items, &ctx));
897 assert!(seq_type.matches_sequence(&one_item, &ctx));
898 assert!(seq_type.matches_sequence(&three_items, &ctx));
899 }
900
901 #[test]
902 fn test_cardinality_plus() {
903 let seq_type = SequenceType::plus(ItemType::AtomicType(XmlTypeCode::Integer));
905 let table = NameTable::new();
906 let ctx = XPathContext::new(&table);
907
908 let no_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![];
909 let one_item: Vec<XmlItem<RoXmlNavigator<'static>>> =
910 vec![XmlItem::Atomic(XmlValue::integer(BigInt::from(42)))];
911 let three_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![
912 XmlItem::Atomic(XmlValue::integer(BigInt::from(1))),
913 XmlItem::Atomic(XmlValue::integer(BigInt::from(2))),
914 XmlItem::Atomic(XmlValue::integer(BigInt::from(3))),
915 ];
916
917 assert!(!seq_type.matches_sequence(&no_items, &ctx));
918 assert!(seq_type.matches_sequence(&one_item, &ctx));
919 assert!(seq_type.matches_sequence(&three_items, &ctx));
920 }
921
922 #[test]
923 fn test_atomic_integer_match() {
924 let item_type = ItemType::AtomicType(XmlTypeCode::Integer);
926 let table = NameTable::new();
927 let ctx = XPathContext::new(&table);
928
929 let int_value: XmlItem<RoXmlNavigator<'static>> =
930 XmlItem::Atomic(XmlValue::integer(BigInt::from(42)));
931 let str_value: XmlItem<RoXmlNavigator<'static>> =
932 XmlItem::Atomic(XmlValue::string("hello"));
933
934 assert!(item_type.matches_item(&int_value, &ctx));
935 assert!(!item_type.matches_item(&str_value, &ctx));
936 }
937
938 #[test]
939 fn test_atomic_string_match() {
940 let item_type = ItemType::AtomicType(XmlTypeCode::String);
942 let table = NameTable::new();
943 let ctx = XPathContext::new(&table);
944
945 let str_value: XmlItem<RoXmlNavigator<'static>> =
946 XmlItem::Atomic(XmlValue::string("hello"));
947 let int_value: XmlItem<RoXmlNavigator<'static>> =
948 XmlItem::Atomic(XmlValue::integer(BigInt::from(42)));
949
950 assert!(item_type.matches_item(&str_value, &ctx));
951 assert!(!item_type.matches_item(&int_value, &ctx));
952 }
953
954 #[test]
955 fn test_atomic_type_hierarchy() {
956 let item_type = ItemType::AtomicType(XmlTypeCode::AnyAtomicType);
958 let table = NameTable::new();
959 let ctx = XPathContext::new(&table);
960
961 let str_value: XmlItem<RoXmlNavigator<'static>> =
962 XmlItem::Atomic(XmlValue::string("hello"));
963 let int_value: XmlItem<RoXmlNavigator<'static>> =
964 XmlItem::Atomic(XmlValue::integer(BigInt::from(42)));
965 let bool_value: XmlItem<RoXmlNavigator<'static>> = XmlItem::Atomic(XmlValue::boolean(true));
966
967 assert!(item_type.matches_item(&str_value, &ctx));
968 assert!(item_type.matches_item(&int_value, &ctx));
969 assert!(item_type.matches_item(&bool_value, &ctx));
970 }
971
972 #[test]
973 fn test_item_matches_any() {
974 let item_type = ItemType::AnyItem;
976 let table = NameTable::new();
977 let ctx = XPathContext::new(&table);
978
979 let doc = roxmltree::Document::parse("<root/>").expect("parse xml");
980 let mut nav = RoXmlNavigator::new(&doc);
981 nav.move_to_first_child();
982
983 let node_item = XmlItem::Node(nav);
984 let atomic_item: XmlItem<RoXmlNavigator<'_>> = XmlItem::Atomic(XmlValue::string("hello"));
985
986 assert!(item_type.matches_item(&node_item, &ctx));
987 assert!(item_type.matches_item(&atomic_item, &ctx));
988 }
989
990 #[test]
991 fn test_node_rejects_atomic() {
992 let item_type = ItemType::AnyNode;
994 let table = NameTable::new();
995 let ctx = XPathContext::new(&table);
996
997 let atomic_item: XmlItem<RoXmlNavigator<'static>> =
998 XmlItem::Atomic(XmlValue::string("hello"));
999
1000 assert!(!item_type.matches_item(&atomic_item, &ctx));
1001 }
1002
1003 #[test]
1004 fn test_atomic_rejects_node() {
1005 let item_type = ItemType::AtomicType(XmlTypeCode::Integer);
1007 let table = NameTable::new();
1008 let ctx = XPathContext::new(&table);
1009
1010 let doc = roxmltree::Document::parse("<root/>").expect("parse xml");
1011 let mut nav = RoXmlNavigator::new(&doc);
1012 nav.move_to_first_child();
1013
1014 let node_item = XmlItem::Node(nav);
1015
1016 assert!(!item_type.matches_item(&node_item, &ctx));
1017 }
1018
1019 #[test]
1020 fn test_element_item_type_matches_element() {
1021 let item_type = ItemType::Element(None, None);
1023 let table = NameTable::new();
1024 let ctx = XPathContext::new(&table);
1025
1026 let doc = roxmltree::Document::parse("<root/>").expect("parse xml");
1027 let mut nav = RoXmlNavigator::new(&doc);
1028 nav.move_to_first_child();
1029
1030 let node_item = XmlItem::Node(nav);
1031
1032 assert!(item_type.matches_item(&node_item, &ctx));
1033 }
1034
1035 #[test]
1036 fn test_text_item_type_matches_text() {
1037 let item_type = ItemType::Text;
1039 let table = NameTable::new();
1040 let ctx = XPathContext::new(&table);
1041
1042 let doc = roxmltree::Document::parse("<root>text</root>").expect("parse xml");
1043 let mut nav = RoXmlNavigator::new(&doc);
1044 nav.move_to_first_child(); nav.move_to_first_child(); let node_item = XmlItem::Node(nav);
1048
1049 assert!(item_type.matches_item(&node_item, &ctx));
1050 }
1051
1052 #[test]
1053 fn test_sequence_type_mixed_types_fail() {
1054 let seq_type = SequenceType::star(ItemType::AtomicType(XmlTypeCode::Integer));
1056 let table = NameTable::new();
1057 let ctx = XPathContext::new(&table);
1058
1059 let mixed_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![
1060 XmlItem::Atomic(XmlValue::integer(BigInt::from(1))),
1061 XmlItem::Atomic(XmlValue::string("not an integer")),
1062 ];
1063
1064 assert!(!seq_type.matches_sequence(&mixed_items, &ctx));
1065 }
1066
1067 #[test]
1068 fn test_integer_derived_types() {
1069 let item_type = ItemType::AtomicType(XmlTypeCode::Integer);
1071 let table = NameTable::new();
1072 let ctx = XPathContext::new(&table);
1073
1074 let int_value: XmlItem<RoXmlNavigator<'static>> =
1076 XmlItem::Atomic(XmlValue::integer(BigInt::from(42)));
1077
1078 assert!(item_type.matches_item(&int_value, &ctx));
1079 }
1080
1081 #[test]
1086 fn test_empty_sequence_matches_empty() {
1087 let seq_type = SequenceType::empty();
1089 let table = NameTable::new();
1090 let ctx = XPathContext::new(&table);
1091
1092 let empty: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![];
1093 assert!(seq_type.matches_sequence(&empty, &ctx));
1094 }
1095
1096 #[test]
1097 fn test_empty_sequence_rejects_non_empty() {
1098 let seq_type = SequenceType::empty();
1100 let table = NameTable::new();
1101 let ctx = XPathContext::new(&table);
1102
1103 let one_item: Vec<XmlItem<RoXmlNavigator<'static>>> =
1104 vec![XmlItem::Atomic(XmlValue::integer(BigInt::from(42)))];
1105 let two_items: Vec<XmlItem<RoXmlNavigator<'static>>> = vec![
1106 XmlItem::Atomic(XmlValue::integer(BigInt::from(1))),
1107 XmlItem::Atomic(XmlValue::integer(BigInt::from(2))),
1108 ];
1109
1110 assert!(!seq_type.matches_sequence(&one_item, &ctx));
1111 assert!(!seq_type.matches_sequence(&two_items, &ctx));
1112 }
1113
1114 #[test]
1115 fn test_empty_sequence_display() {
1116 assert_eq!(SequenceType::empty().to_string(), "empty-sequence()");
1118 }
1119
1120 #[test]
1121 fn test_is_empty_sequence() {
1122 assert!(SequenceType::empty().is_empty_sequence());
1123 assert!(!SequenceType::any().is_empty_sequence());
1124 assert!(!SequenceType::integer().is_empty_sequence());
1125 }
1126
1127 #[test]
1132 fn test_document_node_with_element_single_element() {
1133 let item_type = ItemType::Document(Some(Box::new(ItemType::Element(None, None))));
1135 let table = NameTable::new();
1136 let ctx = XPathContext::new(&table);
1137
1138 let doc = roxmltree::Document::parse("<root/>").expect("parse xml");
1139 let nav = RoXmlNavigator::new(&doc);
1140
1141 assert!(item_type.matches_node(&nav, &ctx));
1142 }
1143
1144 #[test]
1145 fn test_document_node_with_element_allows_comments() {
1146 let item_type = ItemType::Document(Some(Box::new(ItemType::Element(None, None))));
1148 let table = NameTable::new();
1149 let ctx = XPathContext::new(&table);
1150
1151 let doc = roxmltree::Document::parse("<!-- comment --><root/><!-- another -->")
1152 .expect("parse xml");
1153 let nav = RoXmlNavigator::new(&doc);
1154
1155 assert!(item_type.matches_node(&nav, &ctx));
1156 }
1157
1158 #[test]
1159 fn test_document_node_with_element_allows_pi() {
1160 let item_type = ItemType::Document(Some(Box::new(ItemType::Element(None, None))));
1162 let table = NameTable::new();
1163 let ctx = XPathContext::new(&table);
1164
1165 let doc = roxmltree::Document::parse("<?target data?><root/>").expect("parse xml");
1166 let nav = RoXmlNavigator::new(&doc);
1167
1168 assert!(item_type.matches_node(&nav, &ctx));
1169 }
1170
1171 #[test]
1172 fn test_document_node_with_element_rejects_no_element() {
1173 let item_type = ItemType::Document(Some(Box::new(ItemType::Element(None, None))));
1175 let table = NameTable::new();
1176 let ctx = XPathContext::new(&table);
1177
1178 let doc = roxmltree::Document::parse("<!-- comment only is invalid XML --><dummy/>")
1181 .expect("parse xml");
1182 let nav = RoXmlNavigator::new(&doc);
1183
1184 assert!(item_type.matches_node(&nav, &ctx));
1186 }
1187
1188 #[test]
1189 fn test_document_node_with_specific_element_name() {
1190 let table = NameTable::new();
1192 let root_id = table.add("root");
1193 let qname = QualifiedName::local(root_id);
1194 let name_test = NameTest::QName(qname);
1195 let item_type =
1196 ItemType::Document(Some(Box::new(ItemType::Element(Some(name_test), None))));
1197 let ctx = XPathContext::new(&table);
1198
1199 let doc = roxmltree::Document::parse("<root/>").expect("parse xml");
1200 let nav = RoXmlNavigator::new(&doc);
1201
1202 assert!(item_type.matches_node(&nav, &ctx));
1203 }
1204
1205 #[test]
1206 fn test_document_node_with_wrong_element_name() {
1207 let table = NameTable::new();
1209 let root_id = table.add("root");
1210 let qname = QualifiedName::local(root_id);
1211 let name_test = NameTest::QName(qname);
1212 let item_type =
1213 ItemType::Document(Some(Box::new(ItemType::Element(Some(name_test), None))));
1214 let ctx = XPathContext::new(&table);
1215
1216 let doc = roxmltree::Document::parse("<other/>").expect("parse xml");
1217 let nav = RoXmlNavigator::new(&doc);
1218
1219 assert!(!item_type.matches_node(&nav, &ctx));
1220 }
1221}