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