1use crate::abi::structs::*;
88use crate::abi::types::*;
89use crate::xml::string::xmlstr_to_string;
90use crate::xml::xpath::ast::{Axis, Expr, NameTest, NodeTest, Step};
91use crate::xml::xpath::parser::parse_xpath;
92use crate::xml::xpath::types::{NodeSet, XPathValue};
93use std::os::raw::c_int;
94use std::ptr;
95
96pub const XSLT_PAT_NO_PRIORITY: f64 = -1.0e9;
98
99#[derive(Debug, Clone)]
109pub(crate) struct XsltPatternStep {
110 pub axis: Axis,
112 pub node_test: NodeTest,
114 pub predicates: Vec<Expr>,
116}
117
118#[derive(Debug, Clone)]
120pub(crate) struct XsltPattern {
121 pub steps: Vec<PatternStepEntry>,
125 pub is_absolute: bool,
127 #[allow(dead_code)]
129 pub original: String,
130 #[allow(dead_code)]
132 pub expr: Expr,
133}
134
135#[derive(Debug, Clone)]
137pub(crate) enum PatternStepEntry {
138 Step(XsltPatternStep),
140 #[allow(dead_code)]
142 DescendantOrSelf,
143}
144
145#[derive(Debug, Clone)]
147pub(crate) struct CompiledPattern {
148 pub patterns: Vec<XsltPattern>,
150}
151
152#[derive(Debug)]
162#[repr(C)]
163pub struct _xsltPattern {
164 _unused: [u8; 0],
165}
166
167#[derive(Debug)]
169#[repr(C)]
170pub struct _xsltPatternStep {
171 _unused: [u8; 0],
172}
173
174pub unsafe fn xsltCompilePattern(pattern: *const xmlChar, _doc: *mut _xmlDoc) -> *mut _xsltPattern {
197 if pattern.is_null() {
198 return ptr::null_mut();
199 }
200
201 let pattern_str = xmlstr_to_string(pattern);
202 if pattern_str.is_empty() {
203 return ptr::null_mut();
204 }
205
206 let compiled = match compile_pattern_string(&pattern_str) {
207 Some(cp) => cp,
208 None => return ptr::null_mut(),
209 };
210
211 let layout = std::alloc::Layout::new::<CompiledPattern>();
213 let ptr = std::alloc::alloc(layout) as *mut CompiledPattern;
214 if ptr.is_null() {
215 return ptr::null_mut();
216 }
217 ptr::write(ptr, compiled);
218 ptr as *mut _xsltPattern
219}
220
221fn compile_pattern_string(pattern_str: &str) -> Option<CompiledPattern> {
223 let expr = parse_xpath(pattern_str).ok()?;
225
226 let patterns = decompose_pattern(&expr, pattern_str)?;
228
229 Some(CompiledPattern { patterns })
230}
231
232fn decompose_pattern(expr: &Expr, original: &str) -> Option<Vec<XsltPattern>> {
237 match expr {
238 Expr::Union(left, right) => {
240 let mut patterns = decompose_pattern(left, original)?;
241 let right_patterns = decompose_pattern(right, original)?;
242 patterns.extend(right_patterns);
243 Some(patterns)
244 }
245 _ => {
247 let pattern = expr_to_pattern(expr, original)?;
248 Some(vec![pattern])
249 }
250 }
251}
252
253fn expr_to_pattern(expr: &Expr, original: &str) -> Option<XsltPattern> {
255 let (steps, is_absolute) = collect_steps(expr)?;
256
257 Some(XsltPattern {
258 steps,
259 is_absolute,
260 original: original.to_string(),
261 expr: expr.clone(),
262 })
263}
264
265fn collect_steps(expr: &Expr) -> Option<(Vec<PatternStepEntry>, bool)> {
270 match expr {
271 Expr::Step(step)
274 if step.axis == Axis::Self_
275 && step.node_test == NodeTest::Node
276 && step.predicates.is_empty() =>
277 {
278 Some((vec![], true))
279 }
280 Expr::Step(step) => {
281 let entry = PatternStepEntry::Step(XsltPatternStep {
282 axis: step.axis,
283 node_test: step.node_test.clone(),
284 predicates: step.predicates.clone(),
285 });
286 Some((vec![entry], false))
287 }
288 Expr::AbsolutePath(inner) => {
289 let (steps, _) = collect_steps(inner)?;
290 Some((steps, true))
291 }
292 Expr::RelativePath(left, right) => {
293 let (mut right_steps, _) = collect_steps(right)?;
296 let (left_steps, left_absolute) = collect_steps(left)?;
297 right_steps.extend(left_steps);
298 Some((right_steps, left_absolute))
299 }
300 Expr::Filter(_expr, _predicates) => {
303 let entry = PatternStepEntry::Step(XsltPatternStep {
307 axis: Axis::Self_,
308 node_test: NodeTest::Node,
309 predicates: vec![],
310 });
311 Some((vec![entry], false))
312 }
313 Expr::FunctionCall { name, args } => {
319 let node_test = match (name.as_str(), args.len()) {
320 ("node", 0) => Some(NodeTest::Node),
321 ("text", 0) => Some(NodeTest::Text),
322 ("comment", 0) => Some(NodeTest::Comment),
323 ("processing-instruction", 0) => Some(NodeTest::ProcessingInstruction(None)),
324 ("processing-instruction", 1) => match &args[0] {
325 Expr::StringLiteral(s) => {
326 Some(NodeTest::ProcessingInstruction(Some(s.clone())))
327 }
328 _ => None,
329 },
330 _ => None,
331 };
332 match node_test {
333 Some(nt) => {
334 let entry = PatternStepEntry::Step(XsltPatternStep {
335 axis: Axis::Child,
336 node_test: nt,
337 predicates: vec![],
338 });
339 Some((vec![entry], false))
340 }
341 None if name == "id" || name == "key" => {
343 let entry = PatternStepEntry::Step(XsltPatternStep {
344 axis: Axis::Self_,
345 node_test: NodeTest::Node,
346 predicates: vec![],
347 });
348 Some((vec![entry], false))
349 }
350 None => None,
351 }
352 }
353 _ => {
354 None
357 }
358 }
359}
360
361pub unsafe fn xsltFreePattern(pattern: *mut _xsltPattern) {
371 if pattern.is_null() {
372 return;
373 }
374 let ptr = pattern as *mut CompiledPattern;
375 ptr::drop_in_place(ptr);
377 let layout = std::alloc::Layout::new::<CompiledPattern>();
378 std::alloc::dealloc(ptr as *mut u8, layout);
379}
380
381pub unsafe fn xsltTestPattern(
401 ctxt: *mut _xsltTransformContext,
402 pattern: *mut _xsltPattern,
403 node: *mut _xmlNode,
404) -> c_int {
405 if pattern.is_null() || node.is_null() {
406 return 0;
407 }
408
409 let compiled = &*(pattern as *const CompiledPattern);
410 let xpath_ctxt = if !ctxt.is_null() {
411 (*ctxt).xpathCtxt
412 } else {
413 ptr::null_mut()
414 };
415
416 for sub_pattern in &compiled.patterns {
417 if match_sub_pattern(sub_pattern, node, xpath_ctxt) {
418 return 1;
419 }
420 }
421
422 0
423}
424
425pub unsafe fn xsltTestMatchPattern(node: *mut _xmlNode, pattern_node: *mut _xmlNode) -> bool {
449 if node.is_null() || pattern_node.is_null() {
450 return false;
451 }
452
453 match_pattern_tree(pattern_node, node)
471}
472
473unsafe fn match_pattern_tree(pattern_node: *mut _xmlNode, node: *mut _xmlNode) -> bool {
475 if pattern_node.is_null() || node.is_null() {
476 return false;
477 }
478
479 let node_ref = &*pattern_node;
480 let name = xmlstr_to_string(node_ref.name);
481
482 match name.as_str() {
483 "|" => {
485 let mut child = node_ref.children;
486 while !child.is_null() {
487 if match_pattern_tree(child, node) {
488 return true;
489 }
490 child = (*child).next;
491 }
492 false
493 }
494 "/" => {
496 let steps = collect_children(pattern_node);
500 if steps.is_empty() {
501 return false;
502 }
503 match_pattern_path(&steps, node)
504 }
505 _ => {
506 match_pattern_step(pattern_node, node)
508 }
509 }
510}
511
512unsafe fn collect_children(pattern_node: *mut _xmlNode) -> Vec<*mut _xmlNode> {
514 let mut children = Vec::new();
515 if pattern_node.is_null() {
516 return children;
517 }
518 let mut child = (*pattern_node).children;
519 while !child.is_null() {
520 children.push(child);
521 child = (*child).next;
522 }
523 children
524}
525
526unsafe fn match_pattern_path(steps: &[*mut _xmlNode], node: *mut _xmlNode) -> bool {
530 if steps.is_empty() {
531 return false;
532 }
533
534 let mut current = node;
535
536 for (i, &step) in steps.iter().enumerate() {
537 if current.is_null() {
538 return false;
539 }
540
541 if !match_pattern_step(step, current) {
542 return false;
543 }
544
545 if i < steps.len() - 1 {
547 current = (*current).parent;
548 }
549 }
550
551 true
552}
553
554unsafe fn match_pattern_step(pattern_node: *mut _xmlNode, node: *mut _xmlNode) -> bool {
556 if pattern_node.is_null() || node.is_null() {
557 return false;
558 }
559
560 let pn = &*pattern_node;
561 let nn = &*node;
562 let step_name = xmlstr_to_string(pn.name);
563 let node_name = xmlstr_to_string(nn.name);
564 let node_type = nn.type_;
565
566 match step_name.as_str() {
567 "*" => {
569 if pn.type_ == 2 {
570 node_type == 2
572 } else {
573 node_type == 1
575 }
576 }
577 "node()" => true,
579 "text()" => node_type == 3 || node_type == 4,
581 "comment()" => node_type == 8,
583 "processing-instruction()" => node_type == 7,
585 s if s.starts_with('@') => {
587 let attr_name = &s[1..];
588 node_type == 2 && node_name == attr_name
589 }
590 s if s.ends_with(":*") => {
592 if node_type != 1 {
593 return false;
594 }
595 let prefix = &s[..s.len() - 2];
596 if let Some(ns) = nn.ns.as_ref() {
597 let ns_prefix = xmlstr_to_string(ns.prefix);
598 ns_prefix == prefix
599 } else {
600 prefix.is_empty()
601 }
602 }
603 s if s.contains(':') && !s.starts_with('@') && !s.ends_with(":*") => {
605 if node_type != 1 {
606 return false;
607 }
608 let parts: Vec<&str> = s.splitn(2, ':').collect();
609 if parts.len() != 2 {
610 return false;
611 }
612 let prefix = parts[0];
613 let local = parts[1];
614 if node_name != local {
615 return false;
616 }
617 if let Some(ns) = nn.ns.as_ref() {
618 let ns_prefix = xmlstr_to_string(ns.prefix);
619 ns_prefix == prefix
620 } else {
621 prefix.is_empty()
622 }
623 }
624 _ => {
626 if node_type == 1 || node_type == 2 {
627 node_name == step_name
628 } else {
629 false
630 }
631 }
632 }
633}
634
635unsafe fn match_sub_pattern(
637 pattern: &XsltPattern,
638 node: *mut _xmlNode,
639 xpath_ctxt: *mut _xmlXPathContext,
640) -> bool {
641 if pattern.steps.is_empty() {
644 return pattern.is_absolute && is_document_node(node);
647 }
648
649 let mut current_node = node;
651
652 for (i, entry) in pattern.steps.iter().enumerate() {
653 match entry {
654 PatternStepEntry::Step(step) => {
655 if !match_step(step, current_node, xpath_ctxt, i == 0) {
656 return false;
657 }
658 if i > 0 {
662 current_node = (*current_node).parent;
664 if current_node.is_null() {
665 return false;
666 }
667 }
668 }
669 PatternStepEntry::DescendantOrSelf => {
670 let remaining: Vec<_> = pattern.steps[i + 1..]
676 .iter()
677 .filter_map(|e| {
678 if let PatternStepEntry::Step(s) = e {
679 Some(s.clone())
680 } else {
681 None
682 }
683 })
684 .collect();
685
686 if remaining.is_empty() {
687 return true;
688 }
689
690 let mut ancestor = current_node;
692 loop {
693 ancestor = (*ancestor).parent;
694 if ancestor.is_null() {
695 return false;
696 }
697 if match_steps_sequence(&remaining, ancestor, xpath_ctxt) {
698 return true;
699 }
700 }
701 }
702 }
703 }
704
705 if pattern.is_absolute {
707 if current_node.is_null() {
709 return true;
710 }
711 let mut n = node;
713 loop {
714 let parent = (*n).parent;
715 if parent.is_null() {
716 break;
717 }
718 n = parent;
719 }
720 return (*n).type_ == 9 || (*n).type_ == 13; }
723
724 true
725}
726
727unsafe fn match_steps_sequence(
729 steps: &[XsltPatternStep],
730 node: *mut _xmlNode,
731 xpath_ctxt: *mut _xmlXPathContext,
732) -> bool {
733 let mut current = node;
734 for (i, step) in steps.iter().enumerate() {
735 if !match_step(step, current, xpath_ctxt, i == 0) {
736 return false;
737 }
738 if i < steps.len() - 1 {
739 current = (*current).parent;
740 if current.is_null() {
741 return false;
742 }
743 }
744 }
745 true
746}
747
748unsafe fn is_document_node(node: *mut _xmlNode) -> bool {
750 if node.is_null() {
751 return false;
752 }
753 (*node).type_ == 9 || (*node).type_ == 13
754}
755
756unsafe fn match_step(
763 step: &XsltPatternStep,
764 node: *mut _xmlNode,
765 xpath_ctxt: *mut _xmlXPathContext,
766 _is_first: bool,
767) -> bool {
768 if node.is_null() {
769 return false;
770 }
771
772 let node_ref = &*node;
773 let node_type = node_ref.type_;
774
775 match step.axis {
777 Axis::Attribute => {
778 if node_type != 2 {
780 return false;
782 }
783 }
784 Axis::Child | Axis::Self_
785 if (node_type == 2 || node_type == 9 || node_type == 13)
788 && step.axis == Axis::Child
790 && node_type != 1
791 && node_type != 3
792 && node_type != 4
793 && node_type != 7
794 && node_type != 8
795 => {
796 return false;
797 }
798 _ => {
799 }
802 }
803
804 if !match_node_test(node, &step.node_test) {
806 return false;
807 }
808
809 if !step.predicates.is_empty() {
811 if xpath_ctxt.is_null() {
812 return true;
815 }
816
817 if !evaluate_predicates(node, &step.predicates, xpath_ctxt) {
818 return false;
819 }
820 }
821
822 true
823}
824
825unsafe fn match_node_test(node: *mut _xmlNode, node_test: &NodeTest) -> bool {
827 if node.is_null() {
828 return false;
829 }
830
831 let node_ref = &*node;
832 let node_type = node_ref.type_;
833
834 match node_test {
835 NodeTest::Node => {
836 true
838 }
839 NodeTest::Text => {
840 node_type == 3 || node_type == 4
842 }
843 NodeTest::Comment => {
844 node_type == 8
846 }
847 NodeTest::ProcessingInstruction(target) => {
848 if node_type != 7 {
850 return false;
852 }
853 if let Some(target) = target {
854 let name = xmlstr_to_string(node_ref.name);
855 name == *target
856 } else {
857 true
858 }
859 }
860 NodeTest::NameTest(name_test) => match_name_test(node, name_test),
861 NodeTest::Wildcard => {
862 node_type == 1
864 }
865 NodeTest::NsWildcard(prefix) => {
866 if node_type != 1 {
868 return false;
869 }
870 if let Some(ns) = node_ref.ns.as_ref() {
871 let ns_prefix = xmlstr_to_string(ns.prefix);
872 ns_prefix == *prefix
873 } else {
874 prefix.is_empty()
875 }
876 }
877 }
878}
879
880unsafe fn match_name_test(node: *mut _xmlNode, name_test: &NameTest) -> bool {
882 if node.is_null() {
883 return false;
884 }
885
886 let node_ref = &*node;
887
888 match name_test {
889 NameTest::Any => {
890 node_ref.type_ == 1 || node_ref.type_ == 2
892 }
893 NameTest::LocalName(local) => {
894 let name = xmlstr_to_string(node_ref.name);
895 name == *local
896 }
897 NameTest::QName { prefix, local } => {
898 let name = xmlstr_to_string(node_ref.name);
899 if name != *local {
900 return false;
901 }
902 if let Some(ns) = node_ref.ns.as_ref() {
904 let ns_prefix = xmlstr_to_string(ns.prefix);
905 ns_prefix == *prefix
906 } else {
907 prefix.is_empty()
908 }
909 }
910 }
911}
912
913unsafe fn evaluate_predicates(
917 node: *mut _xmlNode,
918 predicates: &[Expr],
919 xpath_ctxt: *mut _xmlXPathContext,
920) -> bool {
921 if xpath_ctxt.is_null() {
922 return true; }
924
925 let ctxt = &mut *xpath_ctxt;
927
928 let saved_node = ctxt.node;
930
931 ctxt.node = node;
933
934 let mut result = true;
935
936 for predicate in predicates {
937 let doc = if !ctxt.doc.is_null() {
940 ctxt.doc
941 } else if !node.is_null() {
942 (*node).doc
943 } else {
944 ptr::null_mut()
945 };
946 let mut xpath_ctx = crate::xml::xpath::context::XPathContext::new(doc);
947
948 if !saved_node.is_null() {
950 xpath_ctx.set_context_node(saved_node);
951 }
952
953 if !ctxt.namespaces.is_null() && ctxt.nsNr > 0 {
955 let ns_slice = std::slice::from_raw_parts(ctxt.namespaces, ctxt.nsNr as usize);
956 for ns_ptr in ns_slice {
957 if !ns_ptr.is_null() {
958 let ns = &**ns_ptr;
959 let prefix = xmlstr_to_string(ns.prefix);
960 let href = xmlstr_to_string(ns.href);
961 xpath_ctx.register_namespace(&prefix, &href);
962 }
963 }
964 }
965
966 register_pattern_functions(&mut xpath_ctx);
968
969 let pred_result = crate::xml::xpath::eval::eval(&mut xpath_ctx, predicate);
970
971 match pred_result {
972 Ok(val) => {
973 let matches = match val {
976 XPathValue::Number(n) => {
977 (n - 1.0).abs() < f64::EPSILON
980 }
981 _ => val.as_boolean(),
982 };
983 if !matches {
984 result = false;
985 break;
986 }
987 }
988 Err(_) => {
989 result = false;
990 break;
991 }
992 }
993 }
994
995 ctxt.node = saved_node;
997
998 result
999}
1000
1001fn register_pattern_functions(ctx: &mut crate::xml::xpath::context::XPathContext) {
1003 ctx.register_function("id", |_ctx, _args| {
1005 Ok(XPathValue::NodeSet(NodeSet::new()))
1008 });
1009
1010 ctx.register_function("key", |_ctx, _args| {
1012 Ok(XPathValue::NodeSet(NodeSet::new()))
1015 });
1016}
1017
1018pub unsafe fn xsltDefaultPriority(pattern: *const xmlChar) -> f64 {
1043 if pattern.is_null() {
1044 return 0.5;
1045 }
1046
1047 let pattern_str = xmlstr_to_string(pattern);
1048 if pattern_str.is_empty() {
1049 return 0.5;
1050 }
1051
1052 compute_default_priority(&pattern_str)
1053}
1054
1055fn compute_default_priority(pattern_str: &str) -> f64 {
1057 let expr = match parse_xpath(pattern_str) {
1059 Ok(e) => e,
1060 Err(_) => return 0.5, };
1062
1063 compute_expr_priority(&expr)
1064}
1065
1066fn compute_expr_priority(expr: &Expr) -> f64 {
1068 match expr {
1069 Expr::Union(left, right) => {
1071 let left_p = compute_expr_priority(left);
1072 let right_p = compute_expr_priority(right);
1073 left_p.max(right_p)
1074 }
1075
1076 Expr::AbsolutePath(inner) => compute_expr_priority(inner),
1078
1079 Expr::RelativePath(_, right) => compute_expr_priority(right),
1081
1082 Expr::Step(step) => compute_step_priority(step),
1084
1085 Expr::Filter(primary, _) => compute_expr_priority(primary),
1087
1088 Expr::FunctionCall { name, .. } => {
1090 if name == "id" || name == "key" {
1091 0.0
1092 } else {
1093 match name.as_str() {
1097 "node" => -0.25,
1098 "text" | "comment" | "processing-instruction" => 0.0,
1099 _ => 0.5,
1100 }
1101 }
1102 }
1103
1104 _ => 0.5,
1106 }
1107}
1108
1109fn compute_step_priority(step: &Step) -> f64 {
1111 match &step.node_test {
1112 NodeTest::Node => -0.25,
1114
1115 NodeTest::Text | NodeTest::Comment | NodeTest::ProcessingInstruction(_) => 0.0,
1117
1118 NodeTest::NameTest(name_test) => match name_test {
1121 NameTest::LocalName(_) | NameTest::QName { .. } => {
1122 if step.axis == Axis::Attribute {
1123 0.5
1124 } else {
1125 0.0
1126 }
1127 }
1128 NameTest::Any => {
1129 if step.axis == Axis::Attribute {
1132 0.5
1133 } else {
1134 -0.5
1135 }
1136 }
1137 },
1138
1139 NodeTest::Wildcard => {
1141 if step.axis == Axis::Attribute {
1142 0.5
1143 } else {
1144 -0.5
1145 }
1146 }
1147
1148 NodeTest::NsWildcard(_) => {
1150 if step.axis == Axis::Attribute {
1151 0.5
1152 } else {
1153 -0.5
1154 }
1155 }
1156 }
1157}
1158
1159pub fn is_simple_name_pattern(pattern: &str) -> bool {
1168 let expr = match parse_xpath(pattern) {
1169 Ok(e) => e,
1170 Err(_) => return false,
1171 };
1172
1173 matches!(&expr, Expr::Step(Step {
1174 axis: Axis::Child,
1175 node_test: NodeTest::NameTest(name_test),
1176 predicates,
1177 }) if predicates.is_empty() && !matches!(name_test, NameTest::Any))
1178}
1179
1180pub fn is_union_pattern(pattern: &str) -> bool {
1182 let expr = match parse_xpath(pattern) {
1183 Ok(e) => e,
1184 Err(_) => return false,
1185 };
1186
1187 matches!(&expr, Expr::Union(_, _))
1188}
1189
1190pub fn get_pattern_matched_names(pattern: &str) -> Vec<String> {
1195 let expr = match parse_xpath(pattern) {
1196 Ok(e) => e,
1197 Err(_) => return vec![],
1198 };
1199
1200 let mut names = Vec::new();
1201 collect_matched_names(&expr, &mut names);
1202 names
1203}
1204
1205fn collect_matched_names(expr: &Expr, names: &mut Vec<String>) {
1206 match expr {
1207 Expr::Union(left, right) => {
1208 collect_matched_names(left, names);
1209 collect_matched_names(right, names);
1210 }
1211 Expr::Step(Step {
1212 node_test: NodeTest::NameTest(name_test),
1213 ..
1214 }) => match name_test {
1215 NameTest::LocalName(local) => names.push(local.clone()),
1216 NameTest::QName { prefix, local } => names.push(format!("{}:{}", prefix, local)),
1217 NameTest::Any => names.push("*".to_string()),
1218 },
1219 Expr::Step(Step {
1220 node_test: NodeTest::Wildcard,
1221 ..
1222 }) => {
1223 names.push("*".to_string());
1224 }
1225 Expr::Step(Step {
1226 node_test: NodeTest::NsWildcard(prefix),
1227 ..
1228 }) => {
1229 names.push(format!("{}:*", prefix));
1230 }
1231 _ => {}
1232 }
1233}
1234
1235#[cfg(test)]
1240mod tests {
1241 use super::*;
1242
1243 #[test]
1246 fn test_default_priority_name_test() {
1247 let priority = compute_default_priority("para");
1249 assert!(
1250 (priority - 0.0).abs() < f64::EPSILON,
1251 "Expected 0.0 for name test, got {}",
1252 priority
1253 );
1254 }
1255
1256 #[test]
1257 fn test_default_priority_qname() {
1258 let priority = compute_default_priority("xslt:template");
1260 assert!(
1261 (priority - 0.0).abs() < f64::EPSILON,
1262 "Expected 0.0 for QName, got {}",
1263 priority
1264 );
1265 }
1266
1267 #[test]
1268 fn test_default_priority_node_test() {
1269 let priority = compute_default_priority("node()");
1271 assert!(
1272 (priority - (-0.25)).abs() < f64::EPSILON,
1273 "Expected -0.25 for node(), got {}",
1274 priority
1275 );
1276 }
1277
1278 #[test]
1279 fn test_default_priority_text_test() {
1280 let priority = compute_default_priority("text()");
1282 assert!(
1283 (priority - 0.0).abs() < f64::EPSILON,
1284 "Expected 0.0 for text(), got {}",
1285 priority
1286 );
1287 }
1288
1289 #[test]
1290 fn test_default_priority_comment_test() {
1291 let priority = compute_default_priority("comment()");
1293 assert!(
1294 (priority - 0.0).abs() < f64::EPSILON,
1295 "Expected 0.0 for comment(), got {}",
1296 priority
1297 );
1298 }
1299
1300 #[test]
1301 fn test_default_priority_processing_instruction() {
1302 let priority = compute_default_priority("processing-instruction()");
1304 assert!(
1305 (priority - 0.0).abs() < f64::EPSILON,
1306 "Expected 0.0 for processing-instruction(), got {}",
1307 priority
1308 );
1309 }
1310
1311 #[test]
1312 fn test_default_priority_wildcard() {
1313 let priority = compute_default_priority("*");
1315 assert!(
1316 (priority - (-0.5)).abs() < f64::EPSILON,
1317 "Expected -0.5 for *, got {}",
1318 priority
1319 );
1320 }
1321
1322 #[test]
1323 fn test_default_priority_ns_wildcard() {
1324 let priority = compute_default_priority("ns:*");
1326 assert!(
1327 (priority - (-0.5)).abs() < f64::EPSILON,
1328 "Expected -0.5 for ns:*, got {}",
1329 priority
1330 );
1331 }
1332
1333 #[test]
1334 fn test_default_priority_attribute() {
1335 let priority = compute_default_priority("@attr");
1337 assert!(
1338 (priority - 0.5).abs() < f64::EPSILON,
1339 "Expected 0.5 for @attr, got {}",
1340 priority
1341 );
1342 }
1343
1344 #[test]
1345 fn test_default_priority_attribute_wildcard() {
1346 let priority = compute_default_priority("@*");
1348 assert!(
1349 (priority - 0.5).abs() < f64::EPSILON,
1350 "Expected 0.5 for @*, got {}",
1351 priority
1352 );
1353 }
1354
1355 #[test]
1356 fn test_default_priority_union() {
1357 let priority = compute_default_priority("para | *");
1359 assert!(
1360 (priority - 0.0).abs() < f64::EPSILON,
1361 "Expected 0.0 for union, got {}",
1362 priority
1363 );
1364 }
1365
1366 #[test]
1367 fn test_default_priority_compound_path() {
1368 let priority = compute_default_priority("foo/bar");
1370 assert!(
1371 (priority - 0.0).abs() < f64::EPSILON,
1372 "Expected 0.0 for foo/bar, got {}",
1373 priority
1374 );
1375 }
1376
1377 #[test]
1378 fn test_default_priority_empty() {
1379 let priority = compute_default_priority("");
1381 assert!(
1382 (priority - 0.5).abs() < f64::EPSILON,
1383 "Expected 0.5 for empty pattern, got {}",
1384 priority
1385 );
1386 }
1387
1388 unsafe fn create_test_node(name: &str, type_: c_int) -> *mut _xmlNode {
1392 let layout = std::alloc::Layout::new::<_xmlNode>();
1393 let ptr = std::alloc::alloc_zeroed(layout) as *mut _xmlNode;
1394 if ptr.is_null() {
1395 return ptr::null_mut();
1396 }
1397 let node = &mut *ptr;
1398 node.type_ = type_;
1399 let name_bytes = name.as_bytes();
1401 let name_buf = std::alloc::alloc_zeroed(
1402 std::alloc::Layout::array::<u8>(name_bytes.len() + 1).unwrap(),
1403 );
1404 if !name_buf.is_null() {
1405 std::ptr::copy_nonoverlapping(name_bytes.as_ptr(), name_buf, name_bytes.len());
1406 }
1407 node.name = name_buf as *mut xmlChar;
1408 ptr
1409 }
1410
1411 unsafe fn free_test_node(node: *mut _xmlNode) {
1413 if node.is_null() {
1414 return;
1415 }
1416 if !(*node).name.is_null() {
1417 let name = (*node).name;
1418 let len = crate::abi::exports_xml2::xmlStrlen(name) as usize;
1420 std::alloc::dealloc(
1421 name as *mut u8,
1422 std::alloc::Layout::array::<u8>(len + 1).unwrap(),
1423 );
1424 }
1425 let layout = std::alloc::Layout::new::<_xmlNode>();
1426 std::alloc::dealloc(node as *mut u8, layout);
1427 }
1428
1429 #[test]
1430 fn test_node_test_matching_element() {
1431 unsafe {
1432 let node = create_test_node("para", 1); assert!(!node.is_null());
1434
1435 let name_test = NodeTest::NameTest(NameTest::LocalName("para".to_string()));
1437 assert!(match_node_test(node, &name_test));
1438
1439 let wrong_test = NodeTest::NameTest(NameTest::LocalName("foo".to_string()));
1441 assert!(!match_node_test(node, &wrong_test));
1442
1443 let wildcard = NodeTest::Wildcard;
1445 assert!(match_node_test(node, &wildcard));
1446
1447 let node_test = NodeTest::Node;
1449 assert!(match_node_test(node, &node_test));
1450
1451 let text_test = NodeTest::Text;
1453 assert!(!match_node_test(node, &text_test));
1454
1455 free_test_node(node);
1456 }
1457 }
1458
1459 #[test]
1460 fn test_node_test_matching_text() {
1461 unsafe {
1462 let node = create_test_node("", 3); assert!(!node.is_null());
1464
1465 let text_test = NodeTest::Text;
1466 assert!(match_node_test(node, &text_test));
1467
1468 let node_test = NodeTest::Node;
1469 assert!(match_node_test(node, &node_test));
1470
1471 let comment_test = NodeTest::Comment;
1472 assert!(!match_node_test(node, &comment_test));
1473
1474 let element_test = NodeTest::NameTest(NameTest::LocalName("para".to_string()));
1475 assert!(!match_node_test(node, &element_test));
1476
1477 free_test_node(node);
1478 }
1479 }
1480
1481 #[test]
1482 fn test_compile_and_free_pattern() {
1483 unsafe {
1484 let pattern_str = c"para".as_ptr() as *const xmlChar;
1485 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1486 assert!(!compiled.is_null());
1487 xsltFreePattern(compiled);
1488 }
1489 }
1490
1491 #[test]
1492 fn test_compile_null_pattern() {
1493 unsafe {
1494 let compiled = xsltCompilePattern(ptr::null(), ptr::null_mut());
1495 assert!(compiled.is_null());
1496 }
1497 }
1498
1499 #[test]
1500 fn test_compile_empty_pattern() {
1501 unsafe {
1502 let pattern_str = c"".as_ptr() as *const xmlChar;
1503 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1504 assert!(compiled.is_null());
1505 }
1506 }
1507
1508 #[test]
1509 fn test_free_null_pattern() {
1510 unsafe {
1511 xsltFreePattern(ptr::null_mut());
1512 }
1514 }
1515
1516 #[test]
1517 fn test_is_simple_name_pattern() {
1518 assert!(is_simple_name_pattern("para"));
1519 assert!(is_simple_name_pattern("foo:bar"));
1520 assert!(!is_simple_name_pattern("foo/bar"));
1521 assert!(!is_simple_name_pattern("para | foo"));
1522 assert!(!is_simple_name_pattern("*"));
1523 }
1524
1525 #[test]
1526 fn test_is_union_pattern() {
1527 assert!(is_union_pattern("para | foo"));
1528 assert!(is_union_pattern("para | foo | bar"));
1529 assert!(!is_union_pattern("para"));
1530 assert!(!is_union_pattern("foo/bar"));
1531 }
1532
1533 #[test]
1534 fn test_get_pattern_matched_names() {
1535 let names = get_pattern_matched_names("para");
1536 assert_eq!(names, vec!["para"]);
1537
1538 let names = get_pattern_matched_names("foo | bar");
1539 assert_eq!(names.len(), 2);
1540 assert!(names.contains(&"foo".to_string()));
1541 assert!(names.contains(&"bar".to_string()));
1542
1543 let names = get_pattern_matched_names("foo/bar");
1544 assert!(names.is_empty());
1545 }
1546
1547 #[test]
1548 fn test_compile_union_pattern() {
1549 unsafe {
1550 let pattern_str = c"para | foo".as_ptr() as *const xmlChar;
1551 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1552 assert!(!compiled.is_null());
1553 xsltFreePattern(compiled);
1554 }
1555 }
1556
1557 #[test]
1558 fn test_compile_compound_pattern() {
1559 unsafe {
1560 let pattern_str = c"foo/bar".as_ptr() as *const xmlChar;
1561 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1562 assert!(!compiled.is_null());
1563 xsltFreePattern(compiled);
1564 }
1565 }
1566
1567 #[test]
1568 fn test_compile_absolute_pattern() {
1569 unsafe {
1570 let pattern_str = c"/foo/bar".as_ptr() as *const xmlChar;
1571 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1572 assert!(!compiled.is_null());
1573 xsltFreePattern(compiled);
1574 }
1575 }
1576
1577 #[test]
1578 fn test_compile_attribute_pattern() {
1579 unsafe {
1580 let pattern_str = c"@attr".as_ptr() as *const xmlChar;
1581 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1582 assert!(!compiled.is_null());
1583 xsltFreePattern(compiled);
1584 }
1585 }
1586
1587 #[test]
1588 fn test_compile_wildcard_pattern() {
1589 unsafe {
1590 let pattern_str = c"*".as_ptr() as *const xmlChar;
1591 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1592 assert!(!compiled.is_null());
1593 xsltFreePattern(compiled);
1594 }
1595 }
1596
1597 #[test]
1598 fn test_compile_ns_wildcard_pattern() {
1599 unsafe {
1600 let pattern_str = c"ns:*".as_ptr() as *const xmlChar;
1601 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1602 assert!(!compiled.is_null());
1603 xsltFreePattern(compiled);
1604 }
1605 }
1606
1607 #[test]
1608 fn test_compile_node_test_pattern() {
1609 unsafe {
1610 let pattern_str = c"node()".as_ptr() as *const xmlChar;
1611 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1612 assert!(!compiled.is_null());
1613 xsltFreePattern(compiled);
1614 }
1615 }
1616
1617 #[test]
1618 fn test_compile_text_pattern() {
1619 unsafe {
1620 let pattern_str = c"text()".as_ptr() as *const xmlChar;
1621 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1622 assert!(!compiled.is_null());
1623 xsltFreePattern(compiled);
1624 }
1625 }
1626
1627 #[test]
1628 fn test_compile_comment_pattern() {
1629 unsafe {
1630 let pattern_str = c"comment()".as_ptr() as *const xmlChar;
1631 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1632 assert!(!compiled.is_null());
1633 xsltFreePattern(compiled);
1634 }
1635 }
1636
1637 #[test]
1638 fn test_compile_pi_pattern() {
1639 unsafe {
1640 let pattern_str = c"processing-instruction()".as_ptr() as *const xmlChar;
1641 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1642 assert!(!compiled.is_null());
1643 xsltFreePattern(compiled);
1644 }
1645 }
1646
1647 #[test]
1648 fn test_compile_predicate_pattern() {
1649 unsafe {
1650 let pattern_str = c"para[1]".as_ptr() as *const xmlChar;
1651 let compiled = xsltCompilePattern(pattern_str, ptr::null_mut());
1652 assert!(!compiled.is_null());
1653 xsltFreePattern(compiled);
1654 }
1655 }
1656
1657 #[test]
1658 fn test_decompose_union() {
1659 let expr = parse_xpath("a | b").unwrap();
1660 let patterns = decompose_pattern(&expr, "a | b");
1661 assert!(patterns.is_some());
1662 let patterns = patterns.unwrap();
1663 assert_eq!(patterns.len(), 2);
1664 assert_eq!(patterns[0].original, "a | b");
1665 assert_eq!(patterns[1].original, "a | b");
1666 }
1667
1668 #[test]
1669 fn test_decompose_single() {
1670 let expr = parse_xpath("para").unwrap();
1671 let patterns = decompose_pattern(&expr, "para");
1672 assert!(patterns.is_some());
1673 let patterns = patterns.unwrap();
1674 assert_eq!(patterns.len(), 1);
1675 }
1676
1677 #[test]
1678 fn test_collect_steps_simple() {
1679 let expr = parse_xpath("para").unwrap();
1680 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1681 assert!(!is_absolute);
1682 assert_eq!(steps.len(), 1);
1683 if let PatternStepEntry::Step(step) = &steps[0] {
1684 assert_eq!(step.axis, Axis::Child);
1685 assert!(
1686 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "para")
1687 );
1688 } else {
1689 panic!("Expected Step entry");
1690 }
1691 }
1692
1693 #[test]
1694 fn test_collect_steps_absolute() {
1695 let expr = parse_xpath("/foo/bar").unwrap();
1696 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1697 assert!(is_absolute);
1698 assert_eq!(steps.len(), 2);
1699 }
1700
1701 #[test]
1702 fn test_collect_steps_attribute() {
1703 let expr = parse_xpath("@attr").unwrap();
1704 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1705 assert!(!is_absolute);
1706 assert_eq!(steps.len(), 1);
1707 if let PatternStepEntry::Step(step) = &steps[0] {
1708 assert_eq!(step.axis, Axis::Attribute);
1709 } else {
1710 panic!("Expected Step entry");
1711 }
1712 }
1713
1714 #[test]
1715 fn test_collect_steps_compound() {
1716 let expr = parse_xpath("foo/bar").unwrap();
1717 let (steps, is_absolute) = collect_steps(&expr).unwrap();
1718 assert!(!is_absolute);
1719 assert_eq!(steps.len(), 2);
1720 if let PatternStepEntry::Step(step) = &steps[0] {
1722 assert!(
1723 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "bar")
1724 );
1725 } else {
1726 panic!("Expected Step entry for bar");
1727 }
1728 if let PatternStepEntry::Step(step) = &steps[1] {
1730 assert!(
1731 matches!(&step.node_test, NodeTest::NameTest(NameTest::LocalName(n)) if n == "foo")
1732 );
1733 } else {
1734 panic!("Expected Step entry for foo");
1735 }
1736 }
1737
1738 #[test]
1739 fn test_match_name_test_local() {
1740 unsafe {
1741 let node = create_test_node("para", 1);
1742 assert!(!node.is_null());
1743
1744 assert!(match_name_test(
1745 node,
1746 &NameTest::LocalName("para".to_string())
1747 ));
1748 assert!(!match_name_test(
1749 node,
1750 &NameTest::LocalName("foo".to_string())
1751 ));
1752 assert!(match_name_test(node, &NameTest::Any));
1753
1754 free_test_node(node);
1755 }
1756 }
1757
1758 #[test]
1759 fn test_match_node_test_wildcard() {
1760 unsafe {
1761 let element = create_test_node("para", 1);
1762 let text = create_test_node("", 3);
1763 let comment = create_test_node("", 8);
1764
1765 let wildcard = NodeTest::Wildcard;
1766 assert!(match_node_test(element, &wildcard));
1767 assert!(!match_node_test(text, &wildcard));
1768 assert!(!match_node_test(comment, &wildcard));
1769
1770 free_test_node(element);
1771 free_test_node(text);
1772 free_test_node(comment);
1773 }
1774 }
1775
1776 #[test]
1777 fn test_match_node_test_ns_wildcard() {
1778 unsafe {
1779 let node = create_test_node("para", 1);
1780 let ns_wildcard = NodeTest::NsWildcard("".to_string());
1782 assert!(match_node_test(node, &ns_wildcard));
1783
1784 let ns_wildcard = NodeTest::NsWildcard("foo".to_string());
1785 assert!(!match_node_test(node, &ns_wildcard));
1786
1787 free_test_node(node);
1788 }
1789 }
1790
1791 #[test]
1792 fn test_compute_priority_on_compiled_pattern() {
1793 unsafe {
1794 let pattern_str = c"para".as_ptr() as *const xmlChar;
1796 let priority = xsltDefaultPriority(pattern_str);
1797 assert!(
1798 (priority - 0.0).abs() < f64::EPSILON,
1799 "Expected 0.0 for 'para', got {}",
1800 priority
1801 );
1802
1803 let pattern_str = c"*".as_ptr() as *const xmlChar;
1804 let priority = xsltDefaultPriority(pattern_str);
1805 assert!(
1806 (priority - (-0.5)).abs() < f64::EPSILON,
1807 "Expected -0.5 for '*', got {}",
1808 priority
1809 );
1810
1811 let pattern_str = c"node()".as_ptr() as *const xmlChar;
1812 let priority = xsltDefaultPriority(pattern_str);
1813 assert!(
1814 (priority - (-0.25)).abs() < f64::EPSILON,
1815 "Expected -0.25 for 'node()', got {}",
1816 priority
1817 );
1818
1819 let pattern_str = c"@attr".as_ptr() as *const xmlChar;
1820 let priority = xsltDefaultPriority(pattern_str);
1821 assert!(
1822 (priority - 0.5).abs() < f64::EPSILON,
1823 "Expected 0.5 for '@attr', got {}",
1824 priority
1825 );
1826 }
1827 }
1828
1829 #[test]
1830 fn test_compute_priority_null() {
1831 unsafe {
1832 let priority = xsltDefaultPriority(ptr::null());
1833 assert!(
1834 (priority - 0.5).abs() < f64::EPSILON,
1835 "Expected 0.5 for null pattern, got {}",
1836 priority
1837 );
1838 }
1839 }
1840
1841 #[test]
1842 fn test_compute_priority_empty() {
1843 unsafe {
1844 let pattern_str = c"".as_ptr() as *const xmlChar;
1845 let priority = xsltDefaultPriority(pattern_str);
1846 assert!(
1847 (priority - 0.5).abs() < f64::EPSILON,
1848 "Expected 0.5 for empty pattern, got {}",
1849 priority
1850 );
1851 }
1852 }
1853
1854 #[test]
1855 fn test_xslt_test_pattern_null_args() {
1856 unsafe {
1857 let result = xsltTestPattern(ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
1858 assert_eq!(result, 0);
1859 }
1860 }
1861}