1use crate::abi::structs::*;
26use crate::abi::types::*;
27use crate::xml::string::xmlstr_to_string;
28use crate::xml::xpath::ast::{Axis, Expr, NameTest, NodeTest, Step};
29use crate::xml::xpath::parser::parse_xpath;
30use crate::xml::xpath::types::{NodeSet, XPathValue};
31use std::os::raw::c_int;
32use std::ptr;
33
34pub const XSLT_PAT_NO_PRIORITY: f64 = -1.0e9;
36
37#[derive(Debug, Clone)]
47pub(crate) struct XsltPatternStep {
48 pub axis: Axis,
50 pub node_test: NodeTest,
52 pub predicates: Vec<Expr>,
54}
55
56#[derive(Debug, Clone)]
58pub(crate) struct XsltPattern {
59 pub steps: Vec<PatternStepEntry>,
63 pub is_absolute: bool,
65 pub original: String,
67 pub expr: Expr,
69}
70
71#[derive(Debug, Clone)]
73pub(crate) enum PatternStepEntry {
74 Step(XsltPatternStep),
76 DescendantOrSelf,
78}
79
80#[derive(Debug, Clone)]
82pub(crate) struct CompiledPattern {
83 pub patterns: Vec<XsltPattern>,
85}
86
87#[repr(C)]
97pub struct _xsltPattern {
98 _unused: [u8; 0],
99}
100
101#[repr(C)]
103pub struct _xsltPatternStep {
104 _unused: [u8; 0],
105}
106
107pub unsafe fn xsltCompilePattern(pattern: *const xmlChar, _doc: *mut _xmlDoc) -> *mut _xsltPattern {
130 if pattern.is_null() {
131 return ptr::null_mut();
132 }
133
134 let pattern_str = xmlstr_to_string(pattern);
135 if pattern_str.is_empty() {
136 return ptr::null_mut();
137 }
138
139 let compiled = match compile_pattern_string(&pattern_str) {
140 Some(cp) => cp,
141 None => return ptr::null_mut(),
142 };
143
144 let layout = std::alloc::Layout::new::<CompiledPattern>();
146 let ptr = std::alloc::alloc(layout) as *mut CompiledPattern;
147 if ptr.is_null() {
148 return ptr::null_mut();
149 }
150 ptr::write(ptr, compiled);
151 ptr as *mut _xsltPattern
152}
153
154fn compile_pattern_string(pattern_str: &str) -> Option<CompiledPattern> {
156 let expr = parse_xpath(pattern_str).ok()?;
158
159 let patterns = decompose_pattern(&expr, pattern_str)?;
161
162 Some(CompiledPattern { patterns })
163}
164
165fn decompose_pattern(expr: &Expr, original: &str) -> Option<Vec<XsltPattern>> {
170 match expr {
171 Expr::Union(left, right) => {
173 let mut patterns = decompose_pattern(left, original)?;
174 let right_patterns = decompose_pattern(right, original)?;
175 patterns.extend(right_patterns);
176 Some(patterns)
177 }
178 _ => {
180 let pattern = expr_to_pattern(expr, original)?;
181 Some(vec![pattern])
182 }
183 }
184}
185
186fn expr_to_pattern(expr: &Expr, original: &str) -> Option<XsltPattern> {
188 let (steps, is_absolute) = collect_steps(expr)?;
189
190 Some(XsltPattern {
191 steps,
192 is_absolute,
193 original: original.to_string(),
194 expr: expr.clone(),
195 })
196}
197
198fn collect_steps(expr: &Expr) -> Option<(Vec<PatternStepEntry>, bool)> {
203 match expr {
204 Expr::Step(step)
207 if step.axis == Axis::Self_
208 && step.node_test == NodeTest::Node
209 && step.predicates.is_empty() =>
210 {
211 Some((vec![], true))
212 }
213 Expr::Step(step) => {
214 let entry = PatternStepEntry::Step(XsltPatternStep {
215 axis: step.axis,
216 node_test: step.node_test.clone(),
217 predicates: step.predicates.clone(),
218 });
219 Some((vec![entry], false))
220 }
221 Expr::AbsolutePath(inner) => {
222 let (steps, _) = collect_steps(inner)?;
223 Some((steps, true))
224 }
225 Expr::RelativePath(left, right) => {
226 let (mut right_steps, _) = collect_steps(right)?;
229 let (left_steps, left_absolute) = collect_steps(left)?;
230 right_steps.extend(left_steps);
231 Some((right_steps, left_absolute))
232 }
233 Expr::Filter(_expr, _predicates) => {
236 let entry = PatternStepEntry::Step(XsltPatternStep {
240 axis: Axis::Self_,
241 node_test: NodeTest::Node,
242 predicates: vec![],
243 });
244 Some((vec![entry], false))
245 }
246 Expr::FunctionCall { name, args } => {
252 let node_test = match (name.as_str(), args.len()) {
253 ("node", 0) => Some(NodeTest::Node),
254 ("text", 0) => Some(NodeTest::Text),
255 ("comment", 0) => Some(NodeTest::Comment),
256 ("processing-instruction", 0) => Some(NodeTest::ProcessingInstruction(None)),
257 ("processing-instruction", 1) => match &args[0] {
258 Expr::StringLiteral(s) => {
259 Some(NodeTest::ProcessingInstruction(Some(s.clone())))
260 }
261 _ => None,
262 },
263 _ => None,
264 };
265 match node_test {
266 Some(nt) => {
267 let entry = PatternStepEntry::Step(XsltPatternStep {
268 axis: Axis::Child,
269 node_test: nt,
270 predicates: vec![],
271 });
272 Some((vec![entry], false))
273 }
274 None if name == "id" || name == "key" => {
276 let entry = PatternStepEntry::Step(XsltPatternStep {
277 axis: Axis::Self_,
278 node_test: NodeTest::Node,
279 predicates: vec![],
280 });
281 Some((vec![entry], false))
282 }
283 None => None,
284 }
285 }
286 _ => {
287 None
290 }
291 }
292}
293
294pub unsafe fn xsltFreePattern(pattern: *mut _xsltPattern) {
304 if pattern.is_null() {
305 return;
306 }
307 let ptr = pattern as *mut CompiledPattern;
308 ptr::drop_in_place(ptr);
310 let layout = std::alloc::Layout::new::<CompiledPattern>();
311 std::alloc::dealloc(ptr as *mut u8, layout);
312}
313
314pub unsafe fn xsltTestPattern(
334 ctxt: *mut _xsltTransformContext,
335 pattern: *mut _xsltPattern,
336 node: *mut _xmlNode,
337) -> c_int {
338 if pattern.is_null() || node.is_null() {
339 return 0;
340 }
341
342 let compiled = &*(pattern as *const CompiledPattern);
343 let xpath_ctxt = if !ctxt.is_null() {
344 (*ctxt).xpathCtxt
345 } else {
346 ptr::null_mut()
347 };
348
349 for sub_pattern in &compiled.patterns {
350 if match_sub_pattern(sub_pattern, node, xpath_ctxt) {
351 return 1;
352 }
353 }
354
355 0
356}
357
358pub unsafe fn xsltTestMatchPattern(node: *mut _xmlNode, pattern_node: *mut _xmlNode) -> bool {
382 if node.is_null() || pattern_node.is_null() {
383 return false;
384 }
385
386 match_pattern_tree(pattern_node, node)
404}
405
406unsafe fn match_pattern_tree(pattern_node: *mut _xmlNode, node: *mut _xmlNode) -> bool {
408 if pattern_node.is_null() || node.is_null() {
409 return false;
410 }
411
412 let node_ref = &*pattern_node;
413 let name = xmlstr_to_string(node_ref.name);
414
415 match name.as_str() {
416 "|" => {
418 let mut child = node_ref.children;
419 while !child.is_null() {
420 if match_pattern_tree(child, node) {
421 return true;
422 }
423 child = (*child).next;
424 }
425 false
426 }
427 "/" => {
429 let steps = collect_children(pattern_node);
433 if steps.is_empty() {
434 return false;
435 }
436 match_pattern_path(&steps, node)
437 }
438 _ => {
439 match_pattern_step(pattern_node, node)
441 }
442 }
443}
444
445unsafe fn collect_children(pattern_node: *mut _xmlNode) -> Vec<*mut _xmlNode> {
447 let mut children = Vec::new();
448 if pattern_node.is_null() {
449 return children;
450 }
451 let mut child = (*pattern_node).children;
452 while !child.is_null() {
453 children.push(child);
454 child = (*child).next;
455 }
456 children
457}
458
459unsafe fn match_pattern_path(steps: &[*mut _xmlNode], node: *mut _xmlNode) -> bool {
463 if steps.is_empty() {
464 return false;
465 }
466
467 let mut current = node;
468
469 for (i, &step) in steps.iter().enumerate() {
470 if current.is_null() {
471 return false;
472 }
473
474 if !match_pattern_step(step, current) {
475 return false;
476 }
477
478 if i < steps.len() - 1 {
480 current = (*current).parent;
481 }
482 }
483
484 true
485}
486
487unsafe fn match_pattern_step(pattern_node: *mut _xmlNode, node: *mut _xmlNode) -> bool {
489 if pattern_node.is_null() || node.is_null() {
490 return false;
491 }
492
493 let pn = &*pattern_node;
494 let nn = &*node;
495 let step_name = xmlstr_to_string(pn.name);
496 let node_name = xmlstr_to_string(nn.name);
497 let node_type = nn.type_;
498
499 match step_name.as_str() {
500 "*" => {
502 if pn.type_ == 2 {
503 node_type == 2
505 } else {
506 node_type == 1
508 }
509 }
510 "node()" => true,
512 "text()" => node_type == 3 || node_type == 4,
514 "comment()" => node_type == 8,
516 "processing-instruction()" => node_type == 7,
518 s if s.starts_with('@') => {
520 let attr_name = &s[1..];
521 node_type == 2 && node_name == attr_name
522 }
523 s if s.ends_with(":*") => {
525 if node_type != 1 {
526 return false;
527 }
528 let prefix = &s[..s.len() - 2];
529 if let Some(ns) = nn.ns.as_ref() {
530 let ns_prefix = xmlstr_to_string(ns.prefix);
531 ns_prefix == prefix
532 } else {
533 prefix.is_empty()
534 }
535 }
536 s if s.contains(':') && !s.starts_with('@') && !s.ends_with(":*") => {
538 if node_type != 1 {
539 return false;
540 }
541 let parts: Vec<&str> = s.splitn(2, ':').collect();
542 if parts.len() != 2 {
543 return false;
544 }
545 let prefix = parts[0];
546 let local = parts[1];
547 if node_name != local {
548 return false;
549 }
550 if let Some(ns) = nn.ns.as_ref() {
551 let ns_prefix = xmlstr_to_string(ns.prefix);
552 ns_prefix == prefix
553 } else {
554 prefix.is_empty()
555 }
556 }
557 _ => {
559 if node_type == 1 || node_type == 2 {
560 node_name == step_name
561 } else {
562 false
563 }
564 }
565 }
566}
567
568unsafe fn match_sub_pattern(
570 pattern: &XsltPattern,
571 node: *mut _xmlNode,
572 xpath_ctxt: *mut _xmlXPathContext,
573) -> bool {
574 if pattern.steps.is_empty() {
577 return pattern.is_absolute && is_document_node(node);
580 }
581
582 let mut current_node = node;
584
585 for (i, entry) in pattern.steps.iter().enumerate() {
586 match entry {
587 PatternStepEntry::Step(step) => {
588 if !match_step(step, current_node, xpath_ctxt, i == 0) {
589 return false;
590 }
591 if i > 0 {
595 current_node = (*current_node).parent;
597 if current_node.is_null() {
598 return false;
599 }
600 }
601 }
602 PatternStepEntry::DescendantOrSelf => {
603 let remaining: Vec<_> = pattern.steps[i + 1..]
609 .iter()
610 .filter_map(|e| {
611 if let PatternStepEntry::Step(s) = e {
612 Some(s.clone())
613 } else {
614 None
615 }
616 })
617 .collect();
618
619 if remaining.is_empty() {
620 return true;
621 }
622
623 let mut ancestor = current_node;
625 loop {
626 ancestor = (*ancestor).parent;
627 if ancestor.is_null() {
628 return false;
629 }
630 if match_steps_sequence(&remaining, ancestor, xpath_ctxt) {
631 return true;
632 }
633 }
634 }
635 }
636 }
637
638 if pattern.is_absolute {
640 if current_node.is_null() {
642 return true;
643 }
644 let mut n = node;
646 loop {
647 let parent = (*n).parent;
648 if parent.is_null() {
649 break;
650 }
651 n = parent;
652 }
653 return (*n).type_ == 9 || (*n).type_ == 13; }
656
657 true
658}
659
660unsafe fn match_steps_sequence(
662 steps: &[XsltPatternStep],
663 node: *mut _xmlNode,
664 xpath_ctxt: *mut _xmlXPathContext,
665) -> bool {
666 let mut current = node;
667 for (i, step) in steps.iter().enumerate() {
668 if !match_step(step, current, xpath_ctxt, i == 0) {
669 return false;
670 }
671 if i < steps.len() - 1 {
672 current = (*current).parent;
673 if current.is_null() {
674 return false;
675 }
676 }
677 }
678 true
679}
680
681unsafe fn is_document_node(node: *mut _xmlNode) -> bool {
683 if node.is_null() {
684 return false;
685 }
686 (*node).type_ == 9 || (*node).type_ == 13
687}
688
689unsafe fn match_step(
696 step: &XsltPatternStep,
697 node: *mut _xmlNode,
698 xpath_ctxt: *mut _xmlXPathContext,
699 _is_first: bool,
700) -> bool {
701 if node.is_null() {
702 return false;
703 }
704
705 let node_ref = &*node;
706 let node_type = node_ref.type_;
707
708 match step.axis {
710 Axis::Attribute => {
711 if node_type != 2 {
713 return false;
715 }
716 }
717 Axis::Child | Axis::Self_ => {
718 if node_type == 2 || node_type == 9 || node_type == 13 {
721 if step.axis == Axis::Child
723 && node_type != 1
724 && node_type != 3
725 && node_type != 4
726 && node_type != 7
727 && node_type != 8
728 {
729 return false;
730 }
731 }
732 }
733 _ => {
734 }
737 }
738
739 if !match_node_test(node, &step.node_test) {
741 return false;
742 }
743
744 if !step.predicates.is_empty() {
746 if xpath_ctxt.is_null() {
747 return true;
750 }
751
752 if !evaluate_predicates(node, &step.predicates, xpath_ctxt) {
753 return false;
754 }
755 }
756
757 true
758}
759
760unsafe fn match_node_test(node: *mut _xmlNode, node_test: &NodeTest) -> bool {
762 if node.is_null() {
763 return false;
764 }
765
766 let node_ref = &*node;
767 let node_type = node_ref.type_;
768
769 match node_test {
770 NodeTest::Node => {
771 true
773 }
774 NodeTest::Text => {
775 node_type == 3 || node_type == 4
777 }
778 NodeTest::Comment => {
779 node_type == 8
781 }
782 NodeTest::ProcessingInstruction(target) => {
783 if node_type != 7 {
785 return false;
787 }
788 if let Some(target) = target {
789 let name = xmlstr_to_string(node_ref.name);
790 name == *target
791 } else {
792 true
793 }
794 }
795 NodeTest::NameTest(name_test) => match_name_test(node, name_test),
796 NodeTest::Wildcard => {
797 node_type == 1
799 }
800 NodeTest::NsWildcard(prefix) => {
801 if node_type != 1 {
803 return false;
804 }
805 if let Some(ns) = node_ref.ns.as_ref() {
806 let ns_prefix = xmlstr_to_string(ns.prefix);
807 ns_prefix == *prefix
808 } else {
809 prefix.is_empty()
810 }
811 }
812 }
813}
814
815unsafe fn match_name_test(node: *mut _xmlNode, name_test: &NameTest) -> bool {
817 if node.is_null() {
818 return false;
819 }
820
821 let node_ref = &*node;
822
823 match name_test {
824 NameTest::Any => {
825 node_ref.type_ == 1 || node_ref.type_ == 2
827 }
828 NameTest::LocalName(local) => {
829 let name = xmlstr_to_string(node_ref.name);
830 name == *local
831 }
832 NameTest::QName { prefix, local } => {
833 let name = xmlstr_to_string(node_ref.name);
834 if name != *local {
835 return false;
836 }
837 if let Some(ns) = node_ref.ns.as_ref() {
839 let ns_prefix = xmlstr_to_string(ns.prefix);
840 ns_prefix == *prefix
841 } else {
842 prefix.is_empty()
843 }
844 }
845 }
846}
847
848unsafe fn evaluate_predicates(
852 node: *mut _xmlNode,
853 predicates: &[Expr],
854 xpath_ctxt: *mut _xmlXPathContext,
855) -> bool {
856 if xpath_ctxt.is_null() {
857 return true; }
859
860 let ctxt = &mut *xpath_ctxt;
862
863 let saved_node = ctxt.node;
865
866 ctxt.node = node;
868
869 let mut result = true;
870
871 for predicate in predicates {
872 let doc = if !ctxt.doc.is_null() {
875 ctxt.doc
876 } else if !node.is_null() {
877 (*node).doc
878 } else {
879 ptr::null_mut()
880 };
881 let mut xpath_ctx = crate::xml::xpath::context::XPathContext::new(doc);
882
883 if !saved_node.is_null() {
885 xpath_ctx.set_context_node(saved_node);
886 }
887
888 if !ctxt.namespaces.is_null() && ctxt.nsNr > 0 {
890 let ns_slice = std::slice::from_raw_parts(ctxt.namespaces, ctxt.nsNr as usize);
891 for ns_ptr in ns_slice {
892 if !ns_ptr.is_null() {
893 let ns = &**ns_ptr;
894 let prefix = xmlstr_to_string(ns.prefix);
895 let href = xmlstr_to_string(ns.href);
896 xpath_ctx.register_namespace(&prefix, &href);
897 }
898 }
899 }
900
901 register_pattern_functions(&mut xpath_ctx);
903
904 let pred_result = crate::xml::xpath::eval::eval(&mut xpath_ctx, predicate);
905
906 match pred_result {
907 Ok(val) => {
908 let matches = match val {
911 XPathValue::Number(n) => {
912 (n - 1.0).abs() < f64::EPSILON
915 }
916 _ => val.as_boolean(),
917 };
918 if !matches {
919 result = false;
920 break;
921 }
922 }
923 Err(_) => {
924 result = false;
925 break;
926 }
927 }
928 }
929
930 ctxt.node = saved_node;
932
933 result
934}
935
936fn register_pattern_functions(ctx: &mut crate::xml::xpath::context::XPathContext) {
938 ctx.register_function("id", |_ctx, _args| {
940 Ok(XPathValue::NodeSet(NodeSet::new()))
943 });
944
945 ctx.register_function("key", |_ctx, _args| {
947 Ok(XPathValue::NodeSet(NodeSet::new()))
950 });
951}
952
953pub unsafe fn xsltDefaultPriority(pattern: *const xmlChar) -> f64 {
978 if pattern.is_null() {
979 return 0.5;
980 }
981
982 let pattern_str = xmlstr_to_string(pattern);
983 if pattern_str.is_empty() {
984 return 0.5;
985 }
986
987 compute_default_priority(&pattern_str)
988}
989
990fn compute_default_priority(pattern_str: &str) -> f64 {
992 let expr = match parse_xpath(pattern_str) {
994 Ok(e) => e,
995 Err(_) => return 0.5, };
997
998 compute_expr_priority(&expr)
999}
1000
1001fn compute_expr_priority(expr: &Expr) -> f64 {
1003 match expr {
1004 Expr::Union(left, right) => {
1006 let left_p = compute_expr_priority(left);
1007 let right_p = compute_expr_priority(right);
1008 left_p.max(right_p)
1009 }
1010
1011 Expr::AbsolutePath(inner) => compute_expr_priority(inner),
1013
1014 Expr::RelativePath(_, right) => compute_expr_priority(right),
1016
1017 Expr::Step(step) => compute_step_priority(step),
1019
1020 Expr::Filter(primary, _) => compute_expr_priority(primary),
1022
1023 Expr::FunctionCall { name, .. } => {
1025 if name == "id" || name == "key" {
1026 0.0
1027 } else {
1028 match name.as_str() {
1032 "node" => -0.25,
1033 "text" | "comment" | "processing-instruction" => 0.0,
1034 _ => 0.5,
1035 }
1036 }
1037 }
1038
1039 _ => 0.5,
1041 }
1042}
1043
1044fn compute_step_priority(step: &Step) -> f64 {
1046 match &step.node_test {
1047 NodeTest::Node => -0.25,
1049
1050 NodeTest::Text | NodeTest::Comment | NodeTest::ProcessingInstruction(_) => 0.0,
1052
1053 NodeTest::NameTest(name_test) => match name_test {
1056 NameTest::LocalName(_) | NameTest::QName { .. } => {
1057 if step.axis == Axis::Attribute {
1058 0.5
1059 } else {
1060 0.0
1061 }
1062 }
1063 NameTest::Any => {
1064 if step.axis == Axis::Attribute {
1067 0.5
1068 } else {
1069 -0.5
1070 }
1071 }
1072 },
1073
1074 NodeTest::Wildcard => {
1076 if step.axis == Axis::Attribute {
1077 0.5
1078 } else {
1079 -0.5
1080 }
1081 }
1082
1083 NodeTest::NsWildcard(_) => {
1085 if step.axis == Axis::Attribute {
1086 0.5
1087 } else {
1088 -0.5
1089 }
1090 }
1091 }
1092}
1093
1094pub fn is_simple_name_pattern(pattern: &str) -> bool {
1103 let expr = match parse_xpath(pattern) {
1104 Ok(e) => e,
1105 Err(_) => return false,
1106 };
1107
1108 matches!(&expr, Expr::Step(Step {
1109 axis: Axis::Child,
1110 node_test: NodeTest::NameTest(name_test),
1111 predicates,
1112 }) if predicates.is_empty() && !matches!(name_test, NameTest::Any))
1113}
1114
1115pub fn is_union_pattern(pattern: &str) -> bool {
1117 let expr = match parse_xpath(pattern) {
1118 Ok(e) => e,
1119 Err(_) => return false,
1120 };
1121
1122 matches!(&expr, Expr::Union(_, _))
1123}
1124
1125pub fn get_pattern_matched_names(pattern: &str) -> Vec<String> {
1130 let expr = match parse_xpath(pattern) {
1131 Ok(e) => e,
1132 Err(_) => return vec![],
1133 };
1134
1135 let mut names = Vec::new();
1136 collect_matched_names(&expr, &mut names);
1137 names
1138}
1139
1140fn collect_matched_names(expr: &Expr, names: &mut Vec<String>) {
1141 match expr {
1142 Expr::Union(left, right) => {
1143 collect_matched_names(left, names);
1144 collect_matched_names(right, names);
1145 }
1146 Expr::Step(Step {
1147 node_test: NodeTest::NameTest(name_test),
1148 ..
1149 }) => match name_test {
1150 NameTest::LocalName(local) => names.push(local.clone()),
1151 NameTest::QName { prefix, local } => names.push(format!("{}:{}", prefix, local)),
1152 NameTest::Any => names.push("*".to_string()),
1153 },
1154 Expr::Step(Step {
1155 node_test: NodeTest::Wildcard,
1156 ..
1157 }) => {
1158 names.push("*".to_string());
1159 }
1160 Expr::Step(Step {
1161 node_test: NodeTest::NsWildcard(prefix),
1162 ..
1163 }) => {
1164 names.push(format!("{}:*", prefix));
1165 }
1166 _ => {}
1167 }
1168}
1169
1170#[cfg(test)]
1175mod tests {
1176 use super::*;
1177
1178 #[test]
1181 fn test_default_priority_name_test() {
1182 let priority = compute_default_priority("para");
1184 assert!(
1185 (priority - 0.0).abs() < f64::EPSILON,
1186 "Expected 0.0 for name test, got {}",
1187 priority
1188 );
1189 }
1190
1191 #[test]
1192 fn test_default_priority_qname() {
1193 let priority = compute_default_priority("xslt:template");
1195 assert!(
1196 (priority - 0.0).abs() < f64::EPSILON,
1197 "Expected 0.0 for QName, got {}",
1198 priority
1199 );
1200 }
1201
1202 #[test]
1203 fn test_default_priority_node_test() {
1204 let priority = compute_default_priority("node()");
1206 assert!(
1207 (priority - (-0.25)).abs() < f64::EPSILON,
1208 "Expected -0.25 for node(), got {}",
1209 priority
1210 );
1211 }
1212
1213 #[test]
1214 fn test_default_priority_text_test() {
1215 let priority = compute_default_priority("text()");
1217 assert!(
1218 (priority - 0.0).abs() < f64::EPSILON,
1219 "Expected 0.0 for text(), got {}",
1220 priority
1221 );
1222 }
1223
1224 #[test]
1225 fn test_default_priority_comment_test() {
1226 let priority = compute_default_priority("comment()");
1228 assert!(
1229 (priority - 0.0).abs() < f64::EPSILON,
1230 "Expected 0.0 for comment(), got {}",
1231 priority
1232 );
1233 }
1234
1235 #[test]
1236 fn test_default_priority_processing_instruction() {
1237 let priority = compute_default_priority("processing-instruction()");
1239 assert!(
1240 (priority - 0.0).abs() < f64::EPSILON,
1241 "Expected 0.0 for processing-instruction(), got {}",
1242 priority
1243 );
1244 }
1245
1246 #[test]
1247 fn test_default_priority_wildcard() {
1248 let priority = compute_default_priority("*");
1250 assert!(
1251 (priority - (-0.5)).abs() < f64::EPSILON,
1252 "Expected -0.5 for *, got {}",
1253 priority
1254 );
1255 }
1256
1257 #[test]
1258 fn test_default_priority_ns_wildcard() {
1259 let priority = compute_default_priority("ns:*");
1261 assert!(
1262 (priority - (-0.5)).abs() < f64::EPSILON,
1263 "Expected -0.5 for ns:*, got {}",
1264 priority
1265 );
1266 }
1267
1268 #[test]
1269 fn test_default_priority_attribute() {
1270 let priority = compute_default_priority("@attr");
1272 assert!(
1273 (priority - 0.5).abs() < f64::EPSILON,
1274 "Expected 0.5 for @attr, got {}",
1275 priority
1276 );
1277 }
1278
1279 #[test]
1280 fn test_default_priority_attribute_wildcard() {
1281 let priority = compute_default_priority("@*");
1283 assert!(
1284 (priority - 0.5).abs() < f64::EPSILON,
1285 "Expected 0.5 for @*, got {}",
1286 priority
1287 );
1288 }
1289
1290 #[test]
1291 fn test_default_priority_union() {
1292 let priority = compute_default_priority("para | *");
1294 assert!(
1295 (priority - 0.0).abs() < f64::EPSILON,
1296 "Expected 0.0 for union, got {}",
1297 priority
1298 );
1299 }
1300
1301 #[test]
1302 fn test_default_priority_compound_path() {
1303 let priority = compute_default_priority("foo/bar");
1305 assert!(
1306 (priority - 0.0).abs() < f64::EPSILON,
1307 "Expected 0.0 for foo/bar, got {}",
1308 priority
1309 );
1310 }
1311
1312 #[test]
1313 fn test_default_priority_empty() {
1314 let priority = compute_default_priority("");
1316 assert!(
1317 (priority - 0.5).abs() < f64::EPSILON,
1318 "Expected 0.5 for empty pattern, got {}",
1319 priority
1320 );
1321 }
1322
1323 unsafe fn create_test_node(name: &str, type_: c_int) -> *mut _xmlNode {
1327 let layout = std::alloc::Layout::new::<_xmlNode>();
1328 let ptr = std::alloc::alloc_zeroed(layout) as *mut _xmlNode;
1329 if ptr.is_null() {
1330 return ptr::null_mut();
1331 }
1332 let node = &mut *ptr;
1333 node.type_ = type_;
1334 let name_bytes = name.as_bytes();
1336 let name_buf = std::alloc::alloc_zeroed(
1337 std::alloc::Layout::array::<u8>(name_bytes.len() + 1).unwrap(),
1338 );
1339 if !name_buf.is_null() {
1340 std::ptr::copy_nonoverlapping(name_bytes.as_ptr(), name_buf, name_bytes.len());
1341 }
1342 node.name = name_buf as *mut xmlChar;
1343 ptr
1344 }
1345
1346 unsafe fn free_test_node(node: *mut _xmlNode) {
1348 if node.is_null() {
1349 return;
1350 }
1351 if !(*node).name.is_null() {
1352 let name = (*node).name;
1353 let len = crate::abi::exports_xml2::xmlStrlen(name) as usize;
1355 std::alloc::dealloc(
1356 name as *mut u8,
1357 std::alloc::Layout::array::<u8>(len + 1).unwrap(),
1358 );
1359 }
1360 let layout = std::alloc::Layout::new::<_xmlNode>();
1361 std::alloc::dealloc(node as *mut u8, layout);
1362 }
1363
1364 #[test]
1365 fn test_node_test_matching_element() {
1366 unsafe {
1367 let node = create_test_node("para", 1); assert!(!node.is_null());
1369
1370 let name_test = NodeTest::NameTest(NameTest::LocalName("para".to_string()));
1372 assert!(match_node_test(node, &name_test));
1373
1374 let wrong_test = NodeTest::NameTest(NameTest::LocalName("foo".to_string()));
1376 assert!(!match_node_test(node, &wrong_test));
1377
1378 let wildcard = NodeTest::Wildcard;
1380 assert!(match_node_test(node, &wildcard));
1381
1382 let node_test = NodeTest::Node;
1384 assert!(match_node_test(node, &node_test));
1385
1386 let text_test = NodeTest::Text;
1388 assert!(!match_node_test(node, &text_test));
1389
1390 free_test_node(node);
1391 }
1392 }
1393
1394 #[test]
1395 fn test_node_test_matching_text() {
1396 unsafe {
1397 let node = create_test_node("", 3); assert!(!node.is_null());
1399
1400 let text_test = NodeTest::Text;
1401 assert!(match_node_test(node, &text_test));
1402
1403 let node_test = NodeTest::Node;
1404 assert!(match_node_test(node, &node_test));
1405
1406 let comment_test = NodeTest::Comment;
1407 assert!(!match_node_test(node, &comment_test));
1408
1409 let element_test = NodeTest::NameTest(NameTest::LocalName("para".to_string()));
1410 assert!(!match_node_test(node, &element_test));
1411
1412 free_test_node(node);
1413 }
1414 }
1415
1416 #[test]
1417 fn test_compile_and_free_pattern() {
1418 unsafe {
1419 let pattern_str = "para\0".as_ptr() as *const xmlChar;
1420 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1421 assert!(!compiled.is_null());
1422 xsltFreePattern(compiled);
1423 }
1424 }
1425
1426 #[test]
1427 fn test_compile_null_pattern() {
1428 unsafe {
1429 let compiled = xsltCompilePattern(ptr::null(), ptr::null_mut());
1430 assert!(compiled.is_null());
1431 }
1432 }
1433
1434 #[test]
1435 fn test_compile_empty_pattern() {
1436 unsafe {
1437 let pattern_str = "\0".as_ptr() as *const xmlChar;
1438 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1439 assert!(compiled.is_null());
1440 }
1441 }
1442
1443 #[test]
1444 fn test_free_null_pattern() {
1445 unsafe {
1446 xsltFreePattern(ptr::null_mut());
1447 }
1449 }
1450
1451 #[test]
1452 fn test_is_simple_name_pattern() {
1453 assert!(is_simple_name_pattern("para"));
1454 assert!(is_simple_name_pattern("foo:bar"));
1455 assert!(!is_simple_name_pattern("foo/bar"));
1456 assert!(!is_simple_name_pattern("para | foo"));
1457 assert!(!is_simple_name_pattern("*"));
1458 }
1459
1460 #[test]
1461 fn test_is_union_pattern() {
1462 assert!(is_union_pattern("para | foo"));
1463 assert!(is_union_pattern("para | foo | bar"));
1464 assert!(!is_union_pattern("para"));
1465 assert!(!is_union_pattern("foo/bar"));
1466 }
1467
1468 #[test]
1469 fn test_get_pattern_matched_names() {
1470 let names = get_pattern_matched_names("para");
1471 assert_eq!(names, vec!["para"]);
1472
1473 let names = get_pattern_matched_names("foo | bar");
1474 assert_eq!(names.len(), 2);
1475 assert!(names.contains(&"foo".to_string()));
1476 assert!(names.contains(&"bar".to_string()));
1477
1478 let names = get_pattern_matched_names("foo/bar");
1479 assert!(names.is_empty());
1480 }
1481
1482 #[test]
1483 fn test_compile_union_pattern() {
1484 unsafe {
1485 let pattern_str = "para | foo\0".as_ptr() as *const xmlChar;
1486 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1487 assert!(!compiled.is_null());
1488 xsltFreePattern(compiled);
1489 }
1490 }
1491
1492 #[test]
1493 fn test_compile_compound_pattern() {
1494 unsafe {
1495 let pattern_str = "foo/bar\0".as_ptr() as *const xmlChar;
1496 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1497 assert!(!compiled.is_null());
1498 xsltFreePattern(compiled);
1499 }
1500 }
1501
1502 #[test]
1503 fn test_compile_absolute_pattern() {
1504 unsafe {
1505 let pattern_str = "/foo/bar\0".as_ptr() as *const xmlChar;
1506 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1507 assert!(!compiled.is_null());
1508 xsltFreePattern(compiled);
1509 }
1510 }
1511
1512 #[test]
1513 fn test_compile_attribute_pattern() {
1514 unsafe {
1515 let pattern_str = "@attr\0".as_ptr() as *const xmlChar;
1516 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1517 assert!(!compiled.is_null());
1518 xsltFreePattern(compiled);
1519 }
1520 }
1521
1522 #[test]
1523 fn test_compile_wildcard_pattern() {
1524 unsafe {
1525 let pattern_str = "*\0".as_ptr() as *const xmlChar;
1526 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1527 assert!(!compiled.is_null());
1528 xsltFreePattern(compiled);
1529 }
1530 }
1531
1532 #[test]
1533 fn test_compile_ns_wildcard_pattern() {
1534 unsafe {
1535 let pattern_str = "ns:*\0".as_ptr() as *const xmlChar;
1536 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1537 assert!(!compiled.is_null());
1538 xsltFreePattern(compiled);
1539 }
1540 }
1541
1542 #[test]
1543 fn test_compile_node_test_pattern() {
1544 unsafe {
1545 let pattern_str = "node()\0".as_ptr() as *const xmlChar;
1546 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1547 assert!(!compiled.is_null());
1548 xsltFreePattern(compiled);
1549 }
1550 }
1551
1552 #[test]
1553 fn test_compile_text_pattern() {
1554 unsafe {
1555 let pattern_str = "text()\0".as_ptr() as *const xmlChar;
1556 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1557 assert!(!compiled.is_null());
1558 xsltFreePattern(compiled);
1559 }
1560 }
1561
1562 #[test]
1563 fn test_compile_comment_pattern() {
1564 unsafe {
1565 let pattern_str = "comment()\0".as_ptr() as *const xmlChar;
1566 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1567 assert!(!compiled.is_null());
1568 xsltFreePattern(compiled);
1569 }
1570 }
1571
1572 #[test]
1573 fn test_compile_pi_pattern() {
1574 unsafe {
1575 let pattern_str = "processing-instruction()\0".as_ptr() as *const xmlChar;
1576 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1577 assert!(!compiled.is_null());
1578 xsltFreePattern(compiled);
1579 }
1580 }
1581
1582 #[test]
1583 fn test_compile_predicate_pattern() {
1584 unsafe {
1585 let pattern_str = "para[1]\0".as_ptr() as *const xmlChar;
1586 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1587 assert!(!compiled.is_null());
1588 xsltFreePattern(compiled);
1589 }
1590 }
1591
1592 #[test]
1593 fn test_decompose_union() {
1594 let expr = parse_xpath("a | b").unwrap();
1595 let patterns = decompose_pattern(&expr, "a | b");
1596 assert!(patterns.is_some());
1597 let patterns = patterns.unwrap();
1598 assert_eq!(patterns.len(), 2);
1599 assert_eq!(patterns[0].original, "a | b");
1600 assert_eq!(patterns[1].original, "a | b");
1601 }
1602
1603 #[test]
1604 fn test_decompose_single() {
1605 let expr = parse_xpath("para").unwrap();
1606 let patterns = decompose_pattern(&expr, "para");
1607 assert!(patterns.is_some());
1608 let patterns = patterns.unwrap();
1609 assert_eq!(patterns.len(), 1);
1610 }
1611
1612 #[test]
1613 fn test_collect_steps_simple() {
1614 let expr = parse_xpath("para").unwrap();
1615 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1616 assert!(!is_absolute);
1617 assert_eq!(steps.len(), 1);
1618 if let PatternStepEntry::Step(step) = &steps[0] {
1619 assert_eq!(step.axis, Axis::Child);
1620 assert!(
1621 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "para")
1622 );
1623 } else {
1624 panic!("Expected Step entry");
1625 }
1626 }
1627
1628 #[test]
1629 fn test_collect_steps_absolute() {
1630 let expr = parse_xpath("/foo/bar").unwrap();
1631 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1632 assert!(is_absolute);
1633 assert_eq!(steps.len(), 2);
1634 }
1635
1636 #[test]
1637 fn test_collect_steps_attribute() {
1638 let expr = parse_xpath("@attr").unwrap();
1639 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1640 assert!(!is_absolute);
1641 assert_eq!(steps.len(), 1);
1642 if let PatternStepEntry::Step(step) = &steps[0] {
1643 assert_eq!(step.axis, Axis::Attribute);
1644 } else {
1645 panic!("Expected Step entry");
1646 }
1647 }
1648
1649 #[test]
1650 fn test_collect_steps_compound() {
1651 let expr = parse_xpath("foo/bar").unwrap();
1652 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1653 assert!(!is_absolute);
1654 assert_eq!(steps.len(), 2);
1655 if let PatternStepEntry::Step(step) = &steps[0] {
1657 assert!(
1658 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "bar")
1659 );
1660 } else {
1661 panic!("Expected Step entry for bar");
1662 }
1663 if let PatternStepEntry::Step(step) = &steps[1] {
1665 assert!(
1666 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "foo")
1667 );
1668 } else {
1669 panic!("Expected Step entry for foo");
1670 }
1671 }
1672
1673 #[test]
1674 fn test_match_name_test_local() {
1675 unsafe {
1676 let node = create_test_node("para", 1);
1677 assert!(!node.is_null());
1678
1679 assert!(match_name_test(
1680 node,
1681 &NameTest::LocalName("para".to_string())
1682 ));
1683 assert!(!match_name_test(
1684 node,
1685 &NameTest::LocalName("foo".to_string())
1686 ));
1687 assert!(match_name_test(node, &NameTest::Any));
1688
1689 free_test_node(node);
1690 }
1691 }
1692
1693 #[test]
1694 fn test_match_node_test_wildcard() {
1695 unsafe {
1696 let element = create_test_node("para", 1);
1697 let text = create_test_node("", 3);
1698 let comment = create_test_node("", 8);
1699
1700 let wildcard = NodeTest::Wildcard;
1701 assert!(match_node_test(element, &wildcard));
1702 assert!(!match_node_test(text, &wildcard));
1703 assert!(!match_node_test(comment, &wildcard));
1704
1705 free_test_node(element);
1706 free_test_node(text);
1707 free_test_node(comment);
1708 }
1709 }
1710
1711 #[test]
1712 fn test_match_node_test_ns_wildcard() {
1713 unsafe {
1714 let node = create_test_node("para", 1);
1715 let ns_wildcard = NodeTest::NsWildcard("".to_string());
1717 assert!(match_node_test(node, &ns_wildcard));
1718
1719 let ns_wildcard = NodeTest::NsWildcard("foo".to_string());
1720 assert!(!match_node_test(node, &ns_wildcard));
1721
1722 free_test_node(node);
1723 }
1724 }
1725
1726 #[test]
1727 fn test_compute_priority_on_compiled_pattern() {
1728 unsafe {
1729 let pattern_str = "para\0".as_ptr() as *const xmlChar;
1731 let priority = xsltDefaultPriority(pattern_str);
1732 assert!(
1733 (priority - 0.0).abs() < f64::EPSILON,
1734 "Expected 0.0 for 'para', got {}",
1735 priority
1736 );
1737
1738 let pattern_str = "*\0".as_ptr() as *const xmlChar;
1739 let priority = xsltDefaultPriority(pattern_str);
1740 assert!(
1741 (priority - (-0.5)).abs() < f64::EPSILON,
1742 "Expected -0.5 for '*', got {}",
1743 priority
1744 );
1745
1746 let pattern_str = "node()\0".as_ptr() as *const xmlChar;
1747 let priority = xsltDefaultPriority(pattern_str);
1748 assert!(
1749 (priority - (-0.25)).abs() < f64::EPSILON,
1750 "Expected -0.25 for 'node()', got {}",
1751 priority
1752 );
1753
1754 let pattern_str = "@attr\0".as_ptr() as *const xmlChar;
1755 let priority = xsltDefaultPriority(pattern_str);
1756 assert!(
1757 (priority - 0.5).abs() < f64::EPSILON,
1758 "Expected 0.5 for '@attr', got {}",
1759 priority
1760 );
1761 }
1762 }
1763
1764 #[test]
1765 fn test_compute_priority_null() {
1766 unsafe {
1767 let priority = xsltDefaultPriority(ptr::null());
1768 assert!(
1769 (priority - 0.5).abs() < f64::EPSILON,
1770 "Expected 0.5 for null pattern, got {}",
1771 priority
1772 );
1773 }
1774 }
1775
1776 #[test]
1777 fn test_compute_priority_empty() {
1778 unsafe {
1779 let pattern_str = "\0".as_ptr() as *const xmlChar;
1780 let priority = xsltDefaultPriority(pattern_str);
1781 assert!(
1782 (priority - 0.5).abs() < f64::EPSILON,
1783 "Expected 0.5 for empty pattern, got {}",
1784 priority
1785 );
1786 }
1787 }
1788
1789 #[test]
1790 fn test_xslt_test_pattern_null_args() {
1791 unsafe {
1792 let result = xsltTestPattern(ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
1793 assert_eq!(result, 0);
1794 }
1795 }
1796}