1use crate::abi::structs::_xmlNode;
16use crate::abi::types::xmlChar;
17use std::cmp::Ordering;
18use std::ptr;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum XPathType {
27 NodeSet,
28 String,
29 Number,
30 Boolean,
31 Point,
32 Range,
33 LocationSet,
34 Users,
35 XsltTree,
36 Undefined,
37}
38
39#[derive(Debug, Clone, Copy)]
48pub struct XPathNode(pub *mut _xmlNode);
49
50impl PartialEq for XPathNode {
51 fn eq(&self, other: &Self) -> bool {
52 self.0 == other.0
53 }
54}
55
56impl Eq for XPathNode {}
57
58impl PartialOrd for XPathNode {
59 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
60 Some(self.cmp(other))
61 }
62}
63
64impl Ord for XPathNode {
65 fn cmp(&self, other: &Self) -> Ordering {
66 self.0.cmp(&other.0)
69 }
70}
71
72impl std::hash::Hash for XPathNode {
73 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
74 self.0.hash(state);
75 }
76}
77
78#[derive(Debug, Clone)]
83pub struct NodeSet {
84 nodes: Vec<XPathNode>,
85}
86
87impl NodeSet {
88 pub fn new() -> Self {
89 Self { nodes: Vec::new() }
90 }
91
92 pub fn singleton(node: *mut _xmlNode) -> Self {
93 Self {
94 nodes: vec![XPathNode(node)],
95 }
96 }
97
98 pub fn is_empty(&self) -> bool {
99 self.nodes.is_empty()
100 }
101
102 pub fn len(&self) -> usize {
103 self.nodes.len()
104 }
105
106 pub fn iter(&self) -> impl Iterator<Item = *mut _xmlNode> + '_ {
107 self.nodes.iter().map(|n| n.0)
108 }
109
110 pub fn get(&self, index: usize) -> Option<*mut _xmlNode> {
111 self.nodes.get(index).map(|n| n.0)
112 }
113
114 pub fn first(&self) -> Option<*mut _xmlNode> {
115 self.nodes.first().map(|n| n.0)
116 }
117
118 pub fn last(&self) -> Option<*mut _xmlNode> {
119 self.nodes.last().map(|n| n.0)
120 }
121
122 pub fn contains(&self, node: *mut _xmlNode) -> bool {
123 self.nodes.iter().any(|n| n.0 == node)
124 }
125
126 pub fn push(&mut self, node: *mut _xmlNode) {
128 if !self.nodes.iter().any(|n| n.0 == node) {
129 self.nodes.push(XPathNode(node));
130 self.sort();
131 }
132 }
133
134 pub fn extend(&mut self, other: &NodeSet) {
136 for node in other.iter() {
137 self.push(node);
138 }
139 }
140
141 pub fn sort(&mut self) {
143 self.nodes.sort_by(|a, b| {
144 a.0.cmp(&b.0)
148 });
149 self.nodes.dedup();
150 }
151
152 pub unsafe fn to_raw(&self) -> *mut crate::abi::structs::_xmlNodeSet {
157 let node_max = self.nodes.len();
158 let node_tab = if node_max > 0 {
159 let ptr =
160 crate::abi::allocator::xmlMalloc(node_max * std::mem::size_of::<*mut _xmlNode>())
161 as *mut *mut _xmlNode;
162 if ptr.is_null() {
163 return ptr::null_mut();
164 }
165 for (i, node) in self.nodes.iter().enumerate() {
166 ptr::write(ptr.add(i), node.0);
167 }
168 ptr
169 } else {
170 ptr::null_mut()
171 };
172
173 let raw = crate::abi::allocator::xmlMalloc(std::mem::size_of::<
174 crate::abi::structs::_xmlNodeSet,
175 >()) as *mut crate::abi::structs::_xmlNodeSet;
176 if raw.is_null() {
177 if !node_tab.is_null() {
178 crate::abi::allocator::xmlFree(node_tab as *mut _);
179 }
180 return ptr::null_mut();
181 }
182 ptr::write(
183 raw,
184 crate::abi::structs::_xmlNodeSet {
185 nodeNr: node_max as std::os::raw::c_int,
186 nodeMax: node_max as std::os::raw::c_int,
187 nodeTab: node_tab,
188 },
189 );
190 raw
191 }
192}
193
194impl Default for NodeSet {
195 fn default() -> Self {
196 Self::new()
197 }
198}
199
200#[derive(Debug, Clone)]
202pub enum XPathValue {
203 NodeSet(NodeSet),
204 String(String),
205 Number(f64),
206 Boolean(bool),
207}
208
209impl XPathValue {
210 pub fn xpath_type(&self) -> XPathType {
212 match self {
213 XPathValue::NodeSet(_) => XPathType::NodeSet,
214 XPathValue::String(_) => XPathType::String,
215 XPathValue::Number(_) => XPathType::Number,
216 XPathValue::Boolean(_) => XPathType::Boolean,
217 }
218 }
219
220 pub fn as_boolean(&self) -> bool {
222 match self {
223 XPathValue::NodeSet(ns) => !ns.is_empty(),
224 XPathValue::String(s) => !s.is_empty(),
225 XPathValue::Number(n) => *n != 0.0 && !n.is_nan(),
226 XPathValue::Boolean(b) => *b,
227 }
228 }
229
230 pub fn as_number(&self) -> f64 {
232 match self {
233 XPathValue::NodeSet(ns) => {
234 if let Some(node) = ns.first() {
236 let s = node_string_value(node);
237 string_to_number(&s)
238 } else {
239 f64::NAN
240 }
241 }
242 XPathValue::String(s) => string_to_number(s),
243 XPathValue::Number(n) => *n,
244 XPathValue::Boolean(true) => 1.0,
245 XPathValue::Boolean(false) => 0.0,
246 }
247 }
248
249 pub fn as_string(&self) -> String {
251 match self {
252 XPathValue::NodeSet(ns) => {
253 if let Some(node) = ns.first() {
254 node_string_value(node)
255 } else {
256 String::new()
257 }
258 }
259 XPathValue::String(s) => s.clone(),
260 XPathValue::Number(n) => number_to_string(*n),
261 XPathValue::Boolean(true) => "true".to_string(),
262 XPathValue::Boolean(false) => "false".to_string(),
263 }
264 }
265
266 pub fn as_node_set(&self) -> &NodeSet {
268 match self {
269 XPathValue::NodeSet(ns) => ns,
270 _ => panic!("XPathValue is not a node-set"),
271 }
272 }
273
274 pub fn as_node_set_mut(&mut self) -> &mut NodeSet {
276 match self {
277 XPathValue::NodeSet(ns) => ns,
278 _ => panic!("XPathValue is not a node-set"),
279 }
280 }
281}
282
283pub fn node_string_value(node: *mut _xmlNode) -> String {
295 if node.is_null() {
296 return String::new();
297 }
298
299 unsafe {
300 let node_ref = &*node;
301 match node_ref.type_ {
302 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19
303 | 20 => {}
304 _ => return String::new(),
305 }
306
307 if node_ref.type_ == 1 || node_ref.type_ == 9 || node_ref.type_ == 13 {
309 if node_ref.type_ == 13 {
311 if !node_ref.content.is_null() {
313 return crate::xml::string::xmlstr_to_string(node_ref.content);
314 }
315 return String::new();
316 }
317 let mut result = String::new();
319 collect_text(&mut result, node);
320 return result;
321 }
322
323 if node_ref.type_ == 3 || node_ref.type_ == 4 {
325 if !node_ref.content.is_null() {
326 return crate::xml::string::xmlstr_to_string(node_ref.content);
327 }
328 return String::new();
329 }
330
331 if node_ref.type_ == 7 {
333 if !node_ref.content.is_null() {
335 return crate::xml::string::xmlstr_to_string(node_ref.content);
336 }
337 return String::new();
338 }
339
340 String::new()
341 }
342}
343
344unsafe fn collect_text(result: &mut String, node: *mut _xmlNode) {
346 if node.is_null() {
347 return;
348 }
349 let node_ref = &*node;
350
351 if node_ref.type_ == 3 || node_ref.type_ == 4 {
353 if !node_ref.content.is_null() {
354 result.push_str(&crate::xml::string::xmlstr_to_string(node_ref.content));
355 }
356 return;
357 }
358
359 if node_ref.type_ == 1 || node_ref.type_ == 9 || node_ref.type_ == 19 {
361 let mut child = node_ref.children;
362 while !child.is_null() {
363 collect_text(result, child);
364 child = (*child).next;
365 }
366 }
367}
368
369pub fn string_to_number(s: &str) -> f64 {
375 let s = s.trim();
376 if s.is_empty() {
377 return f64::NAN;
378 }
379 match s {
381 "NaN" => return f64::NAN,
382 "Infinity" | "INF" => return f64::INFINITY,
383 "-Infinity" | "-INF" => return f64::NEG_INFINITY,
384 _ => {}
385 }
386 if let Ok(n) = s.parse::<f64>() {
388 n
389 } else {
390 f64::NAN
391 }
392}
393
394pub fn number_to_string(n: f64) -> String {
405 if n.is_nan() {
406 return "NaN".to_string();
407 }
408 if n.is_infinite() {
409 if n.is_sign_negative() {
410 return "-Infinity".to_string();
411 }
412 return "Infinity".to_string();
413 }
414 if n == 0.0 {
415 return "0".to_string();
416 }
417
418 if n.fract() == 0.0 && n.is_finite() {
420 if n.abs() < 1e16 {
422 return format!("{:.0}", n);
423 }
424 }
425
426 let s = format!("{:.15}", n);
428 let trimmed = s.trim_end_matches('0');
430 if trimmed.ends_with('.') {
432 format!("{}0", trimmed)
433 } else {
434 trimmed.to_string()
435 }
436}
437
438pub unsafe fn compare_document_order(a: *mut _xmlNode, b: *mut _xmlNode) -> Ordering {
451 if a.is_null() && b.is_null() {
452 return Ordering::Equal;
453 }
454 if a.is_null() {
455 return Ordering::Less;
456 }
457 if b.is_null() {
458 return Ordering::Greater;
459 }
460 if a == b {
461 return Ordering::Equal;
462 }
463
464 let depth_a = node_depth(a);
466 let depth_b = node_depth(b);
467
468 if depth_a < depth_b {
470 let mut n = b;
471 for _ in 0..(depth_b - depth_a) {
472 n = (*n).parent;
473 if n.is_null() {
474 break;
475 }
476 }
477 if n == a {
478 return Ordering::Less;
479 }
480 } else if depth_b < depth_a {
481 let mut n = a;
482 for _ in 0..(depth_a - depth_b) {
483 n = (*n).parent;
484 if n.is_null() {
485 break;
486 }
487 }
488 if n == b {
489 return Ordering::Greater;
490 }
491 }
492
493 let mut parent_a = a;
495 let mut parent_b = b;
496
497 let mut d_a = depth_a;
499 let mut d_b = depth_b;
500 while d_a > d_b {
501 parent_a = (*parent_a).parent;
502 d_a -= 1;
503 }
504 while d_b > d_a {
505 parent_b = (*parent_b).parent;
506 d_b -= 1;
507 }
508
509 while (*parent_a).parent != (*parent_b).parent {
511 parent_a = (*parent_a).parent;
512 parent_b = (*parent_b).parent;
513 if parent_a.is_null() || parent_b.is_null() {
514 return a.cmp(&b);
516 }
517 }
518
519 let mut n = (*parent_a).parent;
521 if n.is_null() {
522 return a.cmp(&b);
523 }
524 let mut child = (*n).children;
525 while !child.is_null() {
526 if child == parent_a {
527 return Ordering::Less;
528 }
529 if child == parent_b {
530 return Ordering::Greater;
531 }
532 child = (*child).next;
533 }
534
535 a.cmp(&b)
537}
538
539unsafe fn node_depth(node: *mut _xmlNode) -> usize {
541 let mut depth = 0;
542 let mut n = node;
543 while !(*n).parent.is_null() {
544 depth += 1;
545 n = (*n).parent;
546 }
547 depth
548}
549
550#[cfg(test)]
555mod tests {
556 use super::*;
557
558 #[test]
559 fn test_string_to_number() {
560 assert!(string_to_number("").is_nan());
561 assert!(string_to_number("NaN").is_nan());
562 assert_eq!(string_to_number("42"), 42.0);
563 assert_eq!(string_to_number("-42"), -42.0);
564 assert_eq!(string_to_number("3.14"), 3.14);
565 assert_eq!(string_to_number(" 42 "), 42.0);
566 assert!(string_to_number("true").is_nan());
567 assert!(string_to_number("false").is_nan());
568 assert_eq!(string_to_number("0"), 0.0);
569 }
570
571 #[test]
572 fn test_number_to_string() {
573 assert_eq!(number_to_string(f64::NAN), "NaN");
574 assert_eq!(number_to_string(f64::INFINITY), "Infinity");
575 assert_eq!(number_to_string(f64::NEG_INFINITY), "-Infinity");
576 assert_eq!(number_to_string(0.0), "0");
577 assert_eq!(number_to_string(-0.0), "0");
578 assert_eq!(number_to_string(42.0), "42");
579 assert_eq!(number_to_string(3.14), "3.14");
580 }
581
582 #[test]
583 fn test_value_conversions() {
584 let v = XPathValue::Number(42.0);
585 assert_eq!(v.as_number(), 42.0);
586 assert_eq!(v.as_string(), "42");
587 assert_eq!(v.as_boolean(), true);
588
589 let v = XPathValue::Number(0.0);
590 assert_eq!(v.as_boolean(), false);
591
592 let v = XPathValue::Number(f64::NAN);
593 assert_eq!(v.as_boolean(), false);
594
595 let v = XPathValue::String("hello".into());
596 assert_eq!(v.as_string(), "hello");
597 assert_eq!(v.as_boolean(), true);
598
599 let v = XPathValue::String("".into());
600 assert_eq!(v.as_boolean(), false);
601
602 let v = XPathValue::Boolean(true);
603 assert_eq!(v.as_number(), 1.0);
604 assert_eq!(v.as_string(), "true");
605
606 let v = XPathValue::Boolean(false);
607 assert_eq!(v.as_number(), 0.0);
608 assert_eq!(v.as_string(), "false");
609 }
610
611 #[test]
612 fn test_node_set() {
613 let mut ns = NodeSet::new();
614 assert!(ns.is_empty());
615 assert_eq!(ns.len(), 0);
616 }
617}