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 #[allow(dead_code)]
67 pub original: String,
68 #[allow(dead_code)]
70 pub expr: Expr,
71}
72
73#[derive(Debug, Clone)]
75pub(crate) enum PatternStepEntry {
76 Step(XsltPatternStep),
78 #[allow(dead_code)]
80 DescendantOrSelf,
81}
82
83#[derive(Debug, Clone)]
85pub(crate) struct CompiledPattern {
86 pub patterns: Vec<XsltPattern>,
88}
89
90#[derive(Debug)]
100#[repr(C)]
101pub struct _xsltPattern {
102 _unused: [u8; 0],
103}
104
105#[derive(Debug)]
107#[repr(C)]
108pub struct _xsltPatternStep {
109 _unused: [u8; 0],
110}
111
112pub unsafe fn xsltCompilePattern(pattern: *const xmlChar, _doc: *mut _xmlDoc) -> *mut _xsltPattern {
135 if pattern.is_null() {
136 return ptr::null_mut();
137 }
138
139 let pattern_str = xmlstr_to_string(pattern);
140 if pattern_str.is_empty() {
141 return ptr::null_mut();
142 }
143
144 let compiled = match compile_pattern_string(&pattern_str) {
145 Some(cp) => cp,
146 None => return ptr::null_mut(),
147 };
148
149 let layout = std::alloc::Layout::new::<CompiledPattern>();
151 let ptr = std::alloc::alloc(layout) as *mut CompiledPattern;
152 if ptr.is_null() {
153 return ptr::null_mut();
154 }
155 ptr::write(ptr, compiled);
156 ptr as *mut _xsltPattern
157}
158
159fn compile_pattern_string(pattern_str: &str) -> Option<CompiledPattern> {
161 let expr = parse_xpath(pattern_str).ok()?;
163
164 let patterns = decompose_pattern(&expr, pattern_str)?;
166
167 Some(CompiledPattern { patterns })
168}
169
170fn decompose_pattern(expr: &Expr, original: &str) -> Option<Vec<XsltPattern>> {
175 match expr {
176 Expr::Union(left, right) => {
178 let mut patterns = decompose_pattern(left, original)?;
179 let right_patterns = decompose_pattern(right, original)?;
180 patterns.extend(right_patterns);
181 Some(patterns)
182 }
183 _ => {
185 let pattern = expr_to_pattern(expr, original)?;
186 Some(vec![pattern])
187 }
188 }
189}
190
191fn expr_to_pattern(expr: &Expr, original: &str) -> Option<XsltPattern> {
193 let (steps, is_absolute) = collect_steps(expr)?;
194
195 Some(XsltPattern {
196 steps,
197 is_absolute,
198 original: original.to_string(),
199 expr: expr.clone(),
200 })
201}
202
203fn collect_steps(expr: &Expr) -> Option<(Vec<PatternStepEntry>, bool)> {
208 match expr {
209 Expr::Step(step)
212 if step.axis == Axis::Self_
213 && step.node_test == NodeTest::Node
214 && step.predicates.is_empty() =>
215 {
216 Some((vec![], true))
217 }
218 Expr::Step(step) => {
219 let entry = PatternStepEntry::Step(XsltPatternStep {
220 axis: step.axis,
221 node_test: step.node_test.clone(),
222 predicates: step.predicates.clone(),
223 });
224 Some((vec![entry], false))
225 }
226 Expr::AbsolutePath(inner) => {
227 let (steps, _) = collect_steps(inner)?;
228 Some((steps, true))
229 }
230 Expr::RelativePath(left, right) => {
231 let (mut right_steps, _) = collect_steps(right)?;
234 let (left_steps, left_absolute) = collect_steps(left)?;
235 right_steps.extend(left_steps);
236 Some((right_steps, left_absolute))
237 }
238 Expr::Filter(_expr, _predicates) => {
241 let entry = PatternStepEntry::Step(XsltPatternStep {
245 axis: Axis::Self_,
246 node_test: NodeTest::Node,
247 predicates: vec![],
248 });
249 Some((vec![entry], false))
250 }
251 Expr::FunctionCall { name, args } => {
257 let node_test = match (name.as_str(), args.len()) {
258 ("node", 0) => Some(NodeTest::Node),
259 ("text", 0) => Some(NodeTest::Text),
260 ("comment", 0) => Some(NodeTest::Comment),
261 ("processing-instruction", 0) => Some(NodeTest::ProcessingInstruction(None)),
262 ("processing-instruction", 1) => match &args[0] {
263 Expr::StringLiteral(s) => {
264 Some(NodeTest::ProcessingInstruction(Some(s.clone())))
265 }
266 _ => None,
267 },
268 _ => None,
269 };
270 match node_test {
271 Some(nt) => {
272 let entry = PatternStepEntry::Step(XsltPatternStep {
273 axis: Axis::Child,
274 node_test: nt,
275 predicates: vec![],
276 });
277 Some((vec![entry], false))
278 }
279 None if name == "id" || name == "key" => {
281 let entry = PatternStepEntry::Step(XsltPatternStep {
282 axis: Axis::Self_,
283 node_test: NodeTest::Node,
284 predicates: vec![],
285 });
286 Some((vec![entry], false))
287 }
288 None => None,
289 }
290 }
291 _ => {
292 None
295 }
296 }
297}
298
299pub unsafe fn xsltFreePattern(pattern: *mut _xsltPattern) {
309 if pattern.is_null() {
310 return;
311 }
312 let ptr = pattern as *mut CompiledPattern;
313 ptr::drop_in_place(ptr);
315 let layout = std::alloc::Layout::new::<CompiledPattern>();
316 std::alloc::dealloc(ptr as *mut u8, layout);
317}
318
319pub unsafe fn xsltTestPattern(
339 ctxt: *mut _xsltTransformContext,
340 pattern: *mut _xsltPattern,
341 node: *mut _xmlNode,
342) -> c_int {
343 if pattern.is_null() || node.is_null() {
344 return 0;
345 }
346
347 let compiled = &*(pattern as *const CompiledPattern);
348 let xpath_ctxt = if !ctxt.is_null() {
349 (*ctxt).xpathCtxt
350 } else {
351 ptr::null_mut()
352 };
353
354 for sub_pattern in &compiled.patterns {
355 if match_sub_pattern(sub_pattern, node, xpath_ctxt) {
356 return 1;
357 }
358 }
359
360 0
361}
362
363pub unsafe fn xsltTestMatchPattern(node: *mut _xmlNode, pattern_node: *mut _xmlNode) -> bool {
387 if node.is_null() || pattern_node.is_null() {
388 return false;
389 }
390
391 match_pattern_tree(pattern_node, node)
409}
410
411unsafe fn match_pattern_tree(pattern_node: *mut _xmlNode, node: *mut _xmlNode) -> bool {
413 if pattern_node.is_null() || node.is_null() {
414 return false;
415 }
416
417 let node_ref = &*pattern_node;
418 let name = xmlstr_to_string(node_ref.name);
419
420 match name.as_str() {
421 "|" => {
423 let mut child = node_ref.children;
424 while !child.is_null() {
425 if match_pattern_tree(child, node) {
426 return true;
427 }
428 child = (*child).next;
429 }
430 false
431 }
432 "/" => {
434 let steps = collect_children(pattern_node);
438 if steps.is_empty() {
439 return false;
440 }
441 match_pattern_path(&steps, node)
442 }
443 _ => {
444 match_pattern_step(pattern_node, node)
446 }
447 }
448}
449
450unsafe fn collect_children(pattern_node: *mut _xmlNode) -> Vec<*mut _xmlNode> {
452 let mut children = Vec::new();
453 if pattern_node.is_null() {
454 return children;
455 }
456 let mut child = (*pattern_node).children;
457 while !child.is_null() {
458 children.push(child);
459 child = (*child).next;
460 }
461 children
462}
463
464unsafe fn match_pattern_path(steps: &[*mut _xmlNode], node: *mut _xmlNode) -> bool {
468 if steps.is_empty() {
469 return false;
470 }
471
472 let mut current = node;
473
474 for (i, &step) in steps.iter().enumerate() {
475 if current.is_null() {
476 return false;
477 }
478
479 if !match_pattern_step(step, current) {
480 return false;
481 }
482
483 if i < steps.len() - 1 {
485 current = (*current).parent;
486 }
487 }
488
489 true
490}
491
492unsafe fn match_pattern_step(pattern_node: *mut _xmlNode, node: *mut _xmlNode) -> bool {
494 if pattern_node.is_null() || node.is_null() {
495 return false;
496 }
497
498 let pn = &*pattern_node;
499 let nn = &*node;
500 let step_name = xmlstr_to_string(pn.name);
501 let node_name = xmlstr_to_string(nn.name);
502 let node_type = nn.type_;
503
504 match step_name.as_str() {
505 "*" => {
507 if pn.type_ == 2 {
508 node_type == 2
510 } else {
511 node_type == 1
513 }
514 }
515 "node()" => true,
517 "text()" => node_type == 3 || node_type == 4,
519 "comment()" => node_type == 8,
521 "processing-instruction()" => node_type == 7,
523 s if s.starts_with('@') => {
525 let attr_name = &s[1..];
526 node_type == 2 && node_name == attr_name
527 }
528 s if s.ends_with(":*") => {
530 if node_type != 1 {
531 return false;
532 }
533 let prefix = &s[..s.len() - 2];
534 if let Some(ns) = nn.ns.as_ref() {
535 let ns_prefix = xmlstr_to_string(ns.prefix);
536 ns_prefix == prefix
537 } else {
538 prefix.is_empty()
539 }
540 }
541 s if s.contains(':') && !s.starts_with('@') && !s.ends_with(":*") => {
543 if node_type != 1 {
544 return false;
545 }
546 let parts: Vec<&str> = s.splitn(2, ':').collect();
547 if parts.len() != 2 {
548 return false;
549 }
550 let prefix = parts[0];
551 let local = parts[1];
552 if node_name != local {
553 return false;
554 }
555 if let Some(ns) = nn.ns.as_ref() {
556 let ns_prefix = xmlstr_to_string(ns.prefix);
557 ns_prefix == prefix
558 } else {
559 prefix.is_empty()
560 }
561 }
562 _ => {
564 if node_type == 1 || node_type == 2 {
565 node_name == step_name
566 } else {
567 false
568 }
569 }
570 }
571}
572
573unsafe fn match_sub_pattern(
575 pattern: &XsltPattern,
576 node: *mut _xmlNode,
577 xpath_ctxt: *mut _xmlXPathContext,
578) -> bool {
579 if pattern.steps.is_empty() {
582 return pattern.is_absolute && is_document_node(node);
585 }
586
587 let mut current_node = node;
589
590 for (i, entry) in pattern.steps.iter().enumerate() {
591 match entry {
592 PatternStepEntry::Step(step) => {
593 if !match_step(step, current_node, xpath_ctxt, i == 0) {
594 return false;
595 }
596 if i > 0 {
600 current_node = (*current_node).parent;
602 if current_node.is_null() {
603 return false;
604 }
605 }
606 }
607 PatternStepEntry::DescendantOrSelf => {
608 let remaining: Vec<_> = pattern.steps[i + 1..]
614 .iter()
615 .filter_map(|e| {
616 if let PatternStepEntry::Step(s) = e {
617 Some(s.clone())
618 } else {
619 None
620 }
621 })
622 .collect();
623
624 if remaining.is_empty() {
625 return true;
626 }
627
628 let mut ancestor = current_node;
630 loop {
631 ancestor = (*ancestor).parent;
632 if ancestor.is_null() {
633 return false;
634 }
635 if match_steps_sequence(&remaining, ancestor, xpath_ctxt) {
636 return true;
637 }
638 }
639 }
640 }
641 }
642
643 if pattern.is_absolute {
645 if current_node.is_null() {
647 return true;
648 }
649 let mut n = node;
651 loop {
652 let parent = (*n).parent;
653 if parent.is_null() {
654 break;
655 }
656 n = parent;
657 }
658 return (*n).type_ == 9 || (*n).type_ == 13; }
661
662 true
663}
664
665unsafe fn match_steps_sequence(
667 steps: &[XsltPatternStep],
668 node: *mut _xmlNode,
669 xpath_ctxt: *mut _xmlXPathContext,
670) -> bool {
671 let mut current = node;
672 for (i, step) in steps.iter().enumerate() {
673 if !match_step(step, current, xpath_ctxt, i == 0) {
674 return false;
675 }
676 if i < steps.len() - 1 {
677 current = (*current).parent;
678 if current.is_null() {
679 return false;
680 }
681 }
682 }
683 true
684}
685
686unsafe fn is_document_node(node: *mut _xmlNode) -> bool {
688 if node.is_null() {
689 return false;
690 }
691 (*node).type_ == 9 || (*node).type_ == 13
692}
693
694unsafe fn match_step(
701 step: &XsltPatternStep,
702 node: *mut _xmlNode,
703 xpath_ctxt: *mut _xmlXPathContext,
704 _is_first: bool,
705) -> bool {
706 if node.is_null() {
707 return false;
708 }
709
710 let node_ref = &*node;
711 let node_type = node_ref.type_;
712
713 match step.axis {
715 Axis::Attribute => {
716 if node_type != 2 {
718 return false;
720 }
721 }
722 Axis::Child | Axis::Self_
723 if (node_type == 2 || node_type == 9 || node_type == 13)
726 && step.axis == Axis::Child
728 && node_type != 1
729 && node_type != 3
730 && node_type != 4
731 && node_type != 7
732 && node_type != 8
733 => {
734 return false;
735 }
736 _ => {
737 }
740 }
741
742 if !match_node_test(node, &step.node_test) {
744 return false;
745 }
746
747 if !step.predicates.is_empty() {
749 if xpath_ctxt.is_null() {
750 return true;
753 }
754
755 if !evaluate_predicates(node, &step.predicates, xpath_ctxt) {
756 return false;
757 }
758 }
759
760 true
761}
762
763unsafe fn match_node_test(node: *mut _xmlNode, node_test: &NodeTest) -> bool {
765 if node.is_null() {
766 return false;
767 }
768
769 let node_ref = &*node;
770 let node_type = node_ref.type_;
771
772 match node_test {
773 NodeTest::Node => {
774 true
776 }
777 NodeTest::Text => {
778 node_type == 3 || node_type == 4
780 }
781 NodeTest::Comment => {
782 node_type == 8
784 }
785 NodeTest::ProcessingInstruction(target) => {
786 if node_type != 7 {
788 return false;
790 }
791 if let Some(target) = target {
792 let name = xmlstr_to_string(node_ref.name);
793 name == *target
794 } else {
795 true
796 }
797 }
798 NodeTest::NameTest(name_test) => match_name_test(node, name_test),
799 NodeTest::Wildcard => {
800 node_type == 1
802 }
803 NodeTest::NsWildcard(prefix) => {
804 if node_type != 1 {
806 return false;
807 }
808 if let Some(ns) = node_ref.ns.as_ref() {
809 let ns_prefix = xmlstr_to_string(ns.prefix);
810 ns_prefix == *prefix
811 } else {
812 prefix.is_empty()
813 }
814 }
815 }
816}
817
818unsafe fn match_name_test(node: *mut _xmlNode, name_test: &NameTest) -> bool {
820 if node.is_null() {
821 return false;
822 }
823
824 let node_ref = &*node;
825
826 match name_test {
827 NameTest::Any => {
828 node_ref.type_ == 1 || node_ref.type_ == 2
830 }
831 NameTest::LocalName(local) => {
832 let name = xmlstr_to_string(node_ref.name);
833 name == *local
834 }
835 NameTest::QName { prefix, local } => {
836 let name = xmlstr_to_string(node_ref.name);
837 if name != *local {
838 return false;
839 }
840 if let Some(ns) = node_ref.ns.as_ref() {
842 let ns_prefix = xmlstr_to_string(ns.prefix);
843 ns_prefix == *prefix
844 } else {
845 prefix.is_empty()
846 }
847 }
848 }
849}
850
851unsafe fn evaluate_predicates(
855 node: *mut _xmlNode,
856 predicates: &[Expr],
857 xpath_ctxt: *mut _xmlXPathContext,
858) -> bool {
859 if xpath_ctxt.is_null() {
860 return true; }
862
863 let ctxt = &mut *xpath_ctxt;
865
866 let saved_node = ctxt.node;
868
869 ctxt.node = node;
871
872 let mut result = true;
873
874 for predicate in predicates {
875 let doc = if !ctxt.doc.is_null() {
878 ctxt.doc
879 } else if !node.is_null() {
880 (*node).doc
881 } else {
882 ptr::null_mut()
883 };
884 let mut xpath_ctx = crate::xml::xpath::context::XPathContext::new(doc);
885
886 if !saved_node.is_null() {
888 xpath_ctx.set_context_node(saved_node);
889 }
890
891 if !ctxt.namespaces.is_null() && ctxt.nsNr > 0 {
893 let ns_slice = std::slice::from_raw_parts(ctxt.namespaces, ctxt.nsNr as usize);
894 for ns_ptr in ns_slice {
895 if !ns_ptr.is_null() {
896 let ns = &**ns_ptr;
897 let prefix = xmlstr_to_string(ns.prefix);
898 let href = xmlstr_to_string(ns.href);
899 xpath_ctx.register_namespace(&prefix, &href);
900 }
901 }
902 }
903
904 register_pattern_functions(&mut xpath_ctx);
906
907 let pred_result = crate::xml::xpath::eval::eval(&mut xpath_ctx, predicate);
908
909 match pred_result {
910 Ok(val) => {
911 let matches = match val {
914 XPathValue::Number(n) => {
915 (n - 1.0).abs() < f64::EPSILON
918 }
919 _ => val.as_boolean(),
920 };
921 if !matches {
922 result = false;
923 break;
924 }
925 }
926 Err(_) => {
927 result = false;
928 break;
929 }
930 }
931 }
932
933 ctxt.node = saved_node;
935
936 result
937}
938
939fn register_pattern_functions(ctx: &mut crate::xml::xpath::context::XPathContext) {
941 ctx.register_function("id", |_ctx, _args| {
943 Ok(XPathValue::NodeSet(NodeSet::new()))
946 });
947
948 ctx.register_function("key", |_ctx, _args| {
950 Ok(XPathValue::NodeSet(NodeSet::new()))
953 });
954}
955
956pub unsafe fn xsltDefaultPriority(pattern: *const xmlChar) -> f64 {
981 if pattern.is_null() {
982 return 0.5;
983 }
984
985 let pattern_str = xmlstr_to_string(pattern);
986 if pattern_str.is_empty() {
987 return 0.5;
988 }
989
990 compute_default_priority(&pattern_str)
991}
992
993fn compute_default_priority(pattern_str: &str) -> f64 {
995 let expr = match parse_xpath(pattern_str) {
997 Ok(e) => e,
998 Err(_) => return 0.5, };
1000
1001 compute_expr_priority(&expr)
1002}
1003
1004fn compute_expr_priority(expr: &Expr) -> f64 {
1006 match expr {
1007 Expr::Union(left, right) => {
1009 let left_p = compute_expr_priority(left);
1010 let right_p = compute_expr_priority(right);
1011 left_p.max(right_p)
1012 }
1013
1014 Expr::AbsolutePath(inner) => compute_expr_priority(inner),
1016
1017 Expr::RelativePath(_, right) => compute_expr_priority(right),
1019
1020 Expr::Step(step) => compute_step_priority(step),
1022
1023 Expr::Filter(primary, _) => compute_expr_priority(primary),
1025
1026 Expr::FunctionCall { name, .. } => {
1028 if name == "id" || name == "key" {
1029 0.0
1030 } else {
1031 match name.as_str() {
1035 "node" => -0.25,
1036 "text" | "comment" | "processing-instruction" => 0.0,
1037 _ => 0.5,
1038 }
1039 }
1040 }
1041
1042 _ => 0.5,
1044 }
1045}
1046
1047fn compute_step_priority(step: &Step) -> f64 {
1049 match &step.node_test {
1050 NodeTest::Node => -0.25,
1052
1053 NodeTest::Text | NodeTest::Comment | NodeTest::ProcessingInstruction(_) => 0.0,
1055
1056 NodeTest::NameTest(name_test) => match name_test {
1059 NameTest::LocalName(_) | NameTest::QName { .. } => {
1060 if step.axis == Axis::Attribute {
1061 0.5
1062 } else {
1063 0.0
1064 }
1065 }
1066 NameTest::Any => {
1067 if step.axis == Axis::Attribute {
1070 0.5
1071 } else {
1072 -0.5
1073 }
1074 }
1075 },
1076
1077 NodeTest::Wildcard => {
1079 if step.axis == Axis::Attribute {
1080 0.5
1081 } else {
1082 -0.5
1083 }
1084 }
1085
1086 NodeTest::NsWildcard(_) => {
1088 if step.axis == Axis::Attribute {
1089 0.5
1090 } else {
1091 -0.5
1092 }
1093 }
1094 }
1095}
1096
1097pub fn is_simple_name_pattern(pattern: &str) -> bool {
1106 let expr = match parse_xpath(pattern) {
1107 Ok(e) => e,
1108 Err(_) => return false,
1109 };
1110
1111 matches!(&expr, Expr::Step(Step {
1112 axis: Axis::Child,
1113 node_test: NodeTest::NameTest(name_test),
1114 predicates,
1115 }) if predicates.is_empty() && !matches!(name_test, NameTest::Any))
1116}
1117
1118pub fn is_union_pattern(pattern: &str) -> bool {
1120 let expr = match parse_xpath(pattern) {
1121 Ok(e) => e,
1122 Err(_) => return false,
1123 };
1124
1125 matches!(&expr, Expr::Union(_, _))
1126}
1127
1128pub fn get_pattern_matched_names(pattern: &str) -> Vec<String> {
1133 let expr = match parse_xpath(pattern) {
1134 Ok(e) => e,
1135 Err(_) => return vec![],
1136 };
1137
1138 let mut names = Vec::new();
1139 collect_matched_names(&expr, &mut names);
1140 names
1141}
1142
1143fn collect_matched_names(expr: &Expr, names: &mut Vec<String>) {
1144 match expr {
1145 Expr::Union(left, right) => {
1146 collect_matched_names(left, names);
1147 collect_matched_names(right, names);
1148 }
1149 Expr::Step(Step {
1150 node_test: NodeTest::NameTest(name_test),
1151 ..
1152 }) => match name_test {
1153 NameTest::LocalName(local) => names.push(local.clone()),
1154 NameTest::QName { prefix, local } => names.push(format!("{}:{}", prefix, local)),
1155 NameTest::Any => names.push("*".to_string()),
1156 },
1157 Expr::Step(Step {
1158 node_test: NodeTest::Wildcard,
1159 ..
1160 }) => {
1161 names.push("*".to_string());
1162 }
1163 Expr::Step(Step {
1164 node_test: NodeTest::NsWildcard(prefix),
1165 ..
1166 }) => {
1167 names.push(format!("{}:*", prefix));
1168 }
1169 _ => {}
1170 }
1171}
1172
1173#[cfg(test)]
1178mod tests {
1179 use super::*;
1180
1181 #[test]
1184 fn test_default_priority_name_test() {
1185 let priority = compute_default_priority("para");
1187 assert!(
1188 (priority - 0.0).abs() < f64::EPSILON,
1189 "Expected 0.0 for name test, got {}",
1190 priority
1191 );
1192 }
1193
1194 #[test]
1195 fn test_default_priority_qname() {
1196 let priority = compute_default_priority("xslt:template");
1198 assert!(
1199 (priority - 0.0).abs() < f64::EPSILON,
1200 "Expected 0.0 for QName, got {}",
1201 priority
1202 );
1203 }
1204
1205 #[test]
1206 fn test_default_priority_node_test() {
1207 let priority = compute_default_priority("node()");
1209 assert!(
1210 (priority - (-0.25)).abs() < f64::EPSILON,
1211 "Expected -0.25 for node(), got {}",
1212 priority
1213 );
1214 }
1215
1216 #[test]
1217 fn test_default_priority_text_test() {
1218 let priority = compute_default_priority("text()");
1220 assert!(
1221 (priority - 0.0).abs() < f64::EPSILON,
1222 "Expected 0.0 for text(), got {}",
1223 priority
1224 );
1225 }
1226
1227 #[test]
1228 fn test_default_priority_comment_test() {
1229 let priority = compute_default_priority("comment()");
1231 assert!(
1232 (priority - 0.0).abs() < f64::EPSILON,
1233 "Expected 0.0 for comment(), got {}",
1234 priority
1235 );
1236 }
1237
1238 #[test]
1239 fn test_default_priority_processing_instruction() {
1240 let priority = compute_default_priority("processing-instruction()");
1242 assert!(
1243 (priority - 0.0).abs() < f64::EPSILON,
1244 "Expected 0.0 for processing-instruction(), got {}",
1245 priority
1246 );
1247 }
1248
1249 #[test]
1250 fn test_default_priority_wildcard() {
1251 let priority = compute_default_priority("*");
1253 assert!(
1254 (priority - (-0.5)).abs() < f64::EPSILON,
1255 "Expected -0.5 for *, got {}",
1256 priority
1257 );
1258 }
1259
1260 #[test]
1261 fn test_default_priority_ns_wildcard() {
1262 let priority = compute_default_priority("ns:*");
1264 assert!(
1265 (priority - (-0.5)).abs() < f64::EPSILON,
1266 "Expected -0.5 for ns:*, got {}",
1267 priority
1268 );
1269 }
1270
1271 #[test]
1272 fn test_default_priority_attribute() {
1273 let priority = compute_default_priority("@attr");
1275 assert!(
1276 (priority - 0.5).abs() < f64::EPSILON,
1277 "Expected 0.5 for @attr, got {}",
1278 priority
1279 );
1280 }
1281
1282 #[test]
1283 fn test_default_priority_attribute_wildcard() {
1284 let priority = compute_default_priority("@*");
1286 assert!(
1287 (priority - 0.5).abs() < f64::EPSILON,
1288 "Expected 0.5 for @*, got {}",
1289 priority
1290 );
1291 }
1292
1293 #[test]
1294 fn test_default_priority_union() {
1295 let priority = compute_default_priority("para | *");
1297 assert!(
1298 (priority - 0.0).abs() < f64::EPSILON,
1299 "Expected 0.0 for union, got {}",
1300 priority
1301 );
1302 }
1303
1304 #[test]
1305 fn test_default_priority_compound_path() {
1306 let priority = compute_default_priority("foo/bar");
1308 assert!(
1309 (priority - 0.0).abs() < f64::EPSILON,
1310 "Expected 0.0 for foo/bar, got {}",
1311 priority
1312 );
1313 }
1314
1315 #[test]
1316 fn test_default_priority_empty() {
1317 let priority = compute_default_priority("");
1319 assert!(
1320 (priority - 0.5).abs() < f64::EPSILON,
1321 "Expected 0.5 for empty pattern, got {}",
1322 priority
1323 );
1324 }
1325
1326 unsafe fn create_test_node(name: &str, type_: c_int) -> *mut _xmlNode {
1330 let layout = std::alloc::Layout::new::<_xmlNode>();
1331 let ptr = std::alloc::alloc_zeroed(layout) as *mut _xmlNode;
1332 if ptr.is_null() {
1333 return ptr::null_mut();
1334 }
1335 let node = &mut *ptr;
1336 node.type_ = type_;
1337 let name_bytes = name.as_bytes();
1339 let name_buf = std::alloc::alloc_zeroed(
1340 std::alloc::Layout::array::<u8>(name_bytes.len() + 1).unwrap(),
1341 );
1342 if !name_buf.is_null() {
1343 std::ptr::copy_nonoverlapping(name_bytes.as_ptr(), name_buf, name_bytes.len());
1344 }
1345 node.name = name_buf as *mut xmlChar;
1346 ptr
1347 }
1348
1349 unsafe fn free_test_node(node: *mut _xmlNode) {
1351 if node.is_null() {
1352 return;
1353 }
1354 if !(*node).name.is_null() {
1355 let name = (*node).name;
1356 let len = crate::abi::exports_xml2::xmlStrlen(name) as usize;
1358 std::alloc::dealloc(
1359 name as *mut u8,
1360 std::alloc::Layout::array::<u8>(len + 1).unwrap(),
1361 );
1362 }
1363 let layout = std::alloc::Layout::new::<_xmlNode>();
1364 std::alloc::dealloc(node as *mut u8, layout);
1365 }
1366
1367 #[test]
1368 fn test_node_test_matching_element() {
1369 unsafe {
1370 let node = create_test_node("para", 1); assert!(!node.is_null());
1372
1373 let name_test = NodeTest::NameTest(NameTest::LocalName("para".to_string()));
1375 assert!(match_node_test(node, &name_test));
1376
1377 let wrong_test = NodeTest::NameTest(NameTest::LocalName("foo".to_string()));
1379 assert!(!match_node_test(node, &wrong_test));
1380
1381 let wildcard = NodeTest::Wildcard;
1383 assert!(match_node_test(node, &wildcard));
1384
1385 let node_test = NodeTest::Node;
1387 assert!(match_node_test(node, &node_test));
1388
1389 let text_test = NodeTest::Text;
1391 assert!(!match_node_test(node, &text_test));
1392
1393 free_test_node(node);
1394 }
1395 }
1396
1397 #[test]
1398 fn test_node_test_matching_text() {
1399 unsafe {
1400 let node = create_test_node("", 3); assert!(!node.is_null());
1402
1403 let text_test = NodeTest::Text;
1404 assert!(match_node_test(node, &text_test));
1405
1406 let node_test = NodeTest::Node;
1407 assert!(match_node_test(node, &node_test));
1408
1409 let comment_test = NodeTest::Comment;
1410 assert!(!match_node_test(node, &comment_test));
1411
1412 let element_test = NodeTest::NameTest(NameTest::LocalName("para".to_string()));
1413 assert!(!match_node_test(node, &element_test));
1414
1415 free_test_node(node);
1416 }
1417 }
1418
1419 #[test]
1420 fn test_compile_and_free_pattern() {
1421 unsafe {
1422 let pattern_str = c"para".as_ptr() as *const xmlChar;
1423 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1424 assert!(!compiled.is_null());
1425 xsltFreePattern(compiled);
1426 }
1427 }
1428
1429 #[test]
1430 fn test_compile_null_pattern() {
1431 unsafe {
1432 let compiled = xsltCompilePattern(ptr::null(), ptr::null_mut());
1433 assert!(compiled.is_null());
1434 }
1435 }
1436
1437 #[test]
1438 fn test_compile_empty_pattern() {
1439 unsafe {
1440 let pattern_str = c"".as_ptr() as *const xmlChar;
1441 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1442 assert!(compiled.is_null());
1443 }
1444 }
1445
1446 #[test]
1447 fn test_free_null_pattern() {
1448 unsafe {
1449 xsltFreePattern(ptr::null_mut());
1450 }
1452 }
1453
1454 #[test]
1455 fn test_is_simple_name_pattern() {
1456 assert!(is_simple_name_pattern("para"));
1457 assert!(is_simple_name_pattern("foo:bar"));
1458 assert!(!is_simple_name_pattern("foo/bar"));
1459 assert!(!is_simple_name_pattern("para | foo"));
1460 assert!(!is_simple_name_pattern("*"));
1461 }
1462
1463 #[test]
1464 fn test_is_union_pattern() {
1465 assert!(is_union_pattern("para | foo"));
1466 assert!(is_union_pattern("para | foo | bar"));
1467 assert!(!is_union_pattern("para"));
1468 assert!(!is_union_pattern("foo/bar"));
1469 }
1470
1471 #[test]
1472 fn test_get_pattern_matched_names() {
1473 let names = get_pattern_matched_names("para");
1474 assert_eq!(names, vec!["para"]);
1475
1476 let names = get_pattern_matched_names("foo | bar");
1477 assert_eq!(names.len(), 2);
1478 assert!(names.contains(&"foo".to_string()));
1479 assert!(names.contains(&"bar".to_string()));
1480
1481 let names = get_pattern_matched_names("foo/bar");
1482 assert!(names.is_empty());
1483 }
1484
1485 #[test]
1486 fn test_compile_union_pattern() {
1487 unsafe {
1488 let pattern_str = c"para | foo".as_ptr() as *const xmlChar;
1489 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1490 assert!(!compiled.is_null());
1491 xsltFreePattern(compiled);
1492 }
1493 }
1494
1495 #[test]
1496 fn test_compile_compound_pattern() {
1497 unsafe {
1498 let pattern_str = c"foo/bar".as_ptr() as *const xmlChar;
1499 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1500 assert!(!compiled.is_null());
1501 xsltFreePattern(compiled);
1502 }
1503 }
1504
1505 #[test]
1506 fn test_compile_absolute_pattern() {
1507 unsafe {
1508 let pattern_str = c"/foo/bar".as_ptr() as *const xmlChar;
1509 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1510 assert!(!compiled.is_null());
1511 xsltFreePattern(compiled);
1512 }
1513 }
1514
1515 #[test]
1516 fn test_compile_attribute_pattern() {
1517 unsafe {
1518 let pattern_str = c"@attr".as_ptr() as *const xmlChar;
1519 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1520 assert!(!compiled.is_null());
1521 xsltFreePattern(compiled);
1522 }
1523 }
1524
1525 #[test]
1526 fn test_compile_wildcard_pattern() {
1527 unsafe {
1528 let pattern_str = c"*".as_ptr() as *const xmlChar;
1529 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1530 assert!(!compiled.is_null());
1531 xsltFreePattern(compiled);
1532 }
1533 }
1534
1535 #[test]
1536 fn test_compile_ns_wildcard_pattern() {
1537 unsafe {
1538 let pattern_str = c"ns:*".as_ptr() as *const xmlChar;
1539 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1540 assert!(!compiled.is_null());
1541 xsltFreePattern(compiled);
1542 }
1543 }
1544
1545 #[test]
1546 fn test_compile_node_test_pattern() {
1547 unsafe {
1548 let pattern_str = c"node()".as_ptr() as *const xmlChar;
1549 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1550 assert!(!compiled.is_null());
1551 xsltFreePattern(compiled);
1552 }
1553 }
1554
1555 #[test]
1556 fn test_compile_text_pattern() {
1557 unsafe {
1558 let pattern_str = c"text()".as_ptr() as *const xmlChar;
1559 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1560 assert!(!compiled.is_null());
1561 xsltFreePattern(compiled);
1562 }
1563 }
1564
1565 #[test]
1566 fn test_compile_comment_pattern() {
1567 unsafe {
1568 let pattern_str = c"comment()".as_ptr() as *const xmlChar;
1569 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1570 assert!(!compiled.is_null());
1571 xsltFreePattern(compiled);
1572 }
1573 }
1574
1575 #[test]
1576 fn test_compile_pi_pattern() {
1577 unsafe {
1578 let pattern_str = c"processing-instruction()".as_ptr() as *const xmlChar;
1579 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1580 assert!(!compiled.is_null());
1581 xsltFreePattern(compiled);
1582 }
1583 }
1584
1585 #[test]
1586 fn test_compile_predicate_pattern() {
1587 unsafe {
1588 let pattern_str = c"para[1]".as_ptr() as *const xmlChar;
1589 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1590 assert!(!compiled.is_null());
1591 xsltFreePattern(compiled);
1592 }
1593 }
1594
1595 #[test]
1596 fn test_decompose_union() {
1597 let expr = parse_xpath("a | b").unwrap();
1598 let patterns = decompose_pattern(&expr, "a | b");
1599 assert!(patterns.is_some());
1600 let patterns = patterns.unwrap();
1601 assert_eq!(patterns.len(), 2);
1602 assert_eq!(patterns[0].original, "a | b");
1603 assert_eq!(patterns[1].original, "a | b");
1604 }
1605
1606 #[test]
1607 fn test_decompose_single() {
1608 let expr = parse_xpath("para").unwrap();
1609 let patterns = decompose_pattern(&expr, "para");
1610 assert!(patterns.is_some());
1611 let patterns = patterns.unwrap();
1612 assert_eq!(patterns.len(), 1);
1613 }
1614
1615 #[test]
1616 fn test_collect_steps_simple() {
1617 let expr = parse_xpath("para").unwrap();
1618 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1619 assert!(!is_absolute);
1620 assert_eq!(steps.len(), 1);
1621 if let PatternStepEntry::Step(step) = &steps[0] {
1622 assert_eq!(step.axis, Axis::Child);
1623 assert!(
1624 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "para")
1625 );
1626 } else {
1627 panic!("Expected Step entry");
1628 }
1629 }
1630
1631 #[test]
1632 fn test_collect_steps_absolute() {
1633 let expr = parse_xpath("/foo/bar").unwrap();
1634 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1635 assert!(is_absolute);
1636 assert_eq!(steps.len(), 2);
1637 }
1638
1639 #[test]
1640 fn test_collect_steps_attribute() {
1641 let expr = parse_xpath("@attr").unwrap();
1642 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1643 assert!(!is_absolute);
1644 assert_eq!(steps.len(), 1);
1645 if let PatternStepEntry::Step(step) = &steps[0] {
1646 assert_eq!(step.axis, Axis::Attribute);
1647 } else {
1648 panic!("Expected Step entry");
1649 }
1650 }
1651
1652 #[test]
1653 fn test_collect_steps_compound() {
1654 let expr = parse_xpath("foo/bar").unwrap();
1655 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1656 assert!(!is_absolute);
1657 assert_eq!(steps.len(), 2);
1658 if let PatternStepEntry::Step(step) = &steps[0] {
1660 assert!(
1661 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "bar")
1662 );
1663 } else {
1664 panic!("Expected Step entry for bar");
1665 }
1666 if let PatternStepEntry::Step(step) = &steps[1] {
1668 assert!(
1669 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "foo")
1670 );
1671 } else {
1672 panic!("Expected Step entry for foo");
1673 }
1674 }
1675
1676 #[test]
1677 fn test_match_name_test_local() {
1678 unsafe {
1679 let node = create_test_node("para", 1);
1680 assert!(!node.is_null());
1681
1682 assert!(match_name_test(
1683 node,
1684 &NameTest::LocalName("para".to_string())
1685 ));
1686 assert!(!match_name_test(
1687 node,
1688 &NameTest::LocalName("foo".to_string())
1689 ));
1690 assert!(match_name_test(node, &NameTest::Any));
1691
1692 free_test_node(node);
1693 }
1694 }
1695
1696 #[test]
1697 fn test_match_node_test_wildcard() {
1698 unsafe {
1699 let element = create_test_node("para", 1);
1700 let text = create_test_node("", 3);
1701 let comment = create_test_node("", 8);
1702
1703 let wildcard = NodeTest::Wildcard;
1704 assert!(match_node_test(element, &wildcard));
1705 assert!(!match_node_test(text, &wildcard));
1706 assert!(!match_node_test(comment, &wildcard));
1707
1708 free_test_node(element);
1709 free_test_node(text);
1710 free_test_node(comment);
1711 }
1712 }
1713
1714 #[test]
1715 fn test_match_node_test_ns_wildcard() {
1716 unsafe {
1717 let node = create_test_node("para", 1);
1718 let ns_wildcard = NodeTest::NsWildcard("".to_string());
1720 assert!(match_node_test(node, &ns_wildcard));
1721
1722 let ns_wildcard = NodeTest::NsWildcard("foo".to_string());
1723 assert!(!match_node_test(node, &ns_wildcard));
1724
1725 free_test_node(node);
1726 }
1727 }
1728
1729 #[test]
1730 fn test_compute_priority_on_compiled_pattern() {
1731 unsafe {
1732 let pattern_str = c"para".as_ptr() as *const xmlChar;
1734 let priority = xsltDefaultPriority(pattern_str);
1735 assert!(
1736 (priority - 0.0).abs() < f64::EPSILON,
1737 "Expected 0.0 for 'para', got {}",
1738 priority
1739 );
1740
1741 let pattern_str = c"*".as_ptr() as *const xmlChar;
1742 let priority = xsltDefaultPriority(pattern_str);
1743 assert!(
1744 (priority - (-0.5)).abs() < f64::EPSILON,
1745 "Expected -0.5 for '*', got {}",
1746 priority
1747 );
1748
1749 let pattern_str = c"node()".as_ptr() as *const xmlChar;
1750 let priority = xsltDefaultPriority(pattern_str);
1751 assert!(
1752 (priority - (-0.25)).abs() < f64::EPSILON,
1753 "Expected -0.25 for 'node()', got {}",
1754 priority
1755 );
1756
1757 let pattern_str = c"@attr".as_ptr() as *const xmlChar;
1758 let priority = xsltDefaultPriority(pattern_str);
1759 assert!(
1760 (priority - 0.5).abs() < f64::EPSILON,
1761 "Expected 0.5 for '@attr', got {}",
1762 priority
1763 );
1764 }
1765 }
1766
1767 #[test]
1768 fn test_compute_priority_null() {
1769 unsafe {
1770 let priority = xsltDefaultPriority(ptr::null());
1771 assert!(
1772 (priority - 0.5).abs() < f64::EPSILON,
1773 "Expected 0.5 for null pattern, got {}",
1774 priority
1775 );
1776 }
1777 }
1778
1779 #[test]
1780 fn test_compute_priority_empty() {
1781 unsafe {
1782 let pattern_str = c"".as_ptr() as *const xmlChar;
1783 let priority = xsltDefaultPriority(pattern_str);
1784 assert!(
1785 (priority - 0.5).abs() < f64::EPSILON,
1786 "Expected 0.5 for empty pattern, got {}",
1787 priority
1788 );
1789 }
1790 }
1791
1792 #[test]
1793 fn test_xslt_test_pattern_null_args() {
1794 unsafe {
1795 let result = xsltTestPattern(ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
1796 assert_eq!(result, 0);
1797 }
1798 }
1799}