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) {
152 self.nodes
153 .sort_by(|a, b| unsafe { compare_document_order(a.0, b.0) });
154 self.nodes.dedup();
155 }
156
157 pub unsafe fn to_raw(&self) -> *mut crate::abi::structs::_xmlNodeSet {
162 let node_max = self.nodes.len();
163 let node_tab = if node_max > 0 {
164 let ptr =
165 crate::abi::allocator::xmlMallocImpl(node_max * std::mem::size_of::<*mut _xmlNode>())
166 as *mut *mut _xmlNode;
167 if ptr.is_null() {
168 return ptr::null_mut();
169 }
170 for (i, node) in self.nodes.iter().enumerate() {
171 ptr::write(ptr.add(i), node.0);
172 }
173 ptr
174 } else {
175 ptr::null_mut()
176 };
177
178 let raw = crate::abi::allocator::xmlMallocImpl(std::mem::size_of::<
179 crate::abi::structs::_xmlNodeSet,
180 >()) as *mut crate::abi::structs::_xmlNodeSet;
181 if raw.is_null() {
182 if !node_tab.is_null() {
183 crate::abi::allocator::xmlFreeImpl(node_tab as *mut _);
184 }
185 return ptr::null_mut();
186 }
187 ptr::write(
188 raw,
189 crate::abi::structs::_xmlNodeSet {
190 nodeNr: node_max as std::os::raw::c_int,
191 nodeMax: node_max as std::os::raw::c_int,
192 nodeTab: node_tab,
193 },
194 );
195 raw
196 }
197}
198
199impl Default for NodeSet {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205#[derive(Debug, Clone)]
207pub enum XPathValue {
208 NodeSet(NodeSet),
209 String(String),
210 Number(f64),
211 Boolean(bool),
212}
213
214impl XPathValue {
215 pub fn xpath_type(&self) -> XPathType {
217 match self {
218 XPathValue::NodeSet(_) => XPathType::NodeSet,
219 XPathValue::String(_) => XPathType::String,
220 XPathValue::Number(_) => XPathType::Number,
221 XPathValue::Boolean(_) => XPathType::Boolean,
222 }
223 }
224
225 pub fn as_boolean(&self) -> bool {
227 match self {
228 XPathValue::NodeSet(ns) => !ns.is_empty(),
229 XPathValue::String(s) => !s.is_empty(),
230 XPathValue::Number(n) => *n != 0.0 && !n.is_nan(),
231 XPathValue::Boolean(b) => *b,
232 }
233 }
234
235 pub fn as_number(&self) -> f64 {
237 match self {
238 XPathValue::NodeSet(ns) => {
239 if let Some(node) = ns.first() {
241 let s = node_string_value(node);
242 string_to_number(&s)
243 } else {
244 f64::NAN
245 }
246 }
247 XPathValue::String(s) => string_to_number(s),
248 XPathValue::Number(n) => *n,
249 XPathValue::Boolean(true) => 1.0,
250 XPathValue::Boolean(false) => 0.0,
251 }
252 }
253
254 pub fn as_string(&self) -> String {
256 match self {
257 XPathValue::NodeSet(ns) => {
258 if let Some(node) = ns.first() {
259 node_string_value(node)
260 } else {
261 String::new()
262 }
263 }
264 XPathValue::String(s) => s.clone(),
265 XPathValue::Number(n) => number_to_string(*n),
266 XPathValue::Boolean(true) => "true".to_string(),
267 XPathValue::Boolean(false) => "false".to_string(),
268 }
269 }
270
271 pub fn as_node_set(&self) -> &NodeSet {
273 match self {
274 XPathValue::NodeSet(ns) => ns,
275 _ => panic!("XPathValue is not a node-set"),
276 }
277 }
278
279 pub fn as_node_set_mut(&mut self) -> &mut NodeSet {
281 match self {
282 XPathValue::NodeSet(ns) => ns,
283 _ => panic!("XPathValue is not a node-set"),
284 }
285 }
286}
287
288pub fn node_string_value(node: *mut _xmlNode) -> String {
300 if node.is_null() {
301 return String::new();
302 }
303
304 unsafe {
305 let node_ref = &*node;
306 match node_ref.type_ {
307 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19
308 | 20 => {}
309 _ => return String::new(),
310 }
311
312 if node_ref.type_ == 1 || node_ref.type_ == 9 || node_ref.type_ == 13 {
314 let mut result = String::new();
315 collect_text(&mut result, node);
316 return result;
317 }
318
319 if node_ref.type_ == 2 {
324 if !node_ref.children.is_null() {
325 let child = &*node_ref.children;
326 if (child.type_ == 3 || child.type_ == 4) && !child.content.is_null() {
327 return crate::xml::string::xmlstr_to_string(child.content);
328 }
329 }
330 return String::new();
331 }
332
333 if node_ref.type_ == 3 || node_ref.type_ == 4 {
335 if !node_ref.content.is_null() {
336 return crate::xml::string::xmlstr_to_string(node_ref.content);
337 }
338 return String::new();
339 }
340
341 if node_ref.type_ == 7 {
343 if !node_ref.content.is_null() {
345 return crate::xml::string::xmlstr_to_string(node_ref.content);
346 }
347 return String::new();
348 }
349
350 String::new()
351 }
352}
353
354unsafe fn collect_text(result: &mut String, node: *mut _xmlNode) {
356 if node.is_null() {
357 return;
358 }
359 let node_ref = &*node;
360
361 if node_ref.type_ == 3 || node_ref.type_ == 4 {
363 if !node_ref.content.is_null() {
364 result.push_str(&crate::xml::string::xmlstr_to_string(node_ref.content));
365 }
366 return;
367 }
368
369 if node_ref.type_ == 1 || node_ref.type_ == 9 || node_ref.type_ == 19 {
371 let mut child = node_ref.children;
372 while !child.is_null() {
373 collect_text(result, child);
374 child = (*child).next;
375 }
376 }
377}
378
379pub fn string_to_number(s: &str) -> f64 {
385 let s = s.trim();
386 if s.is_empty() {
387 return f64::NAN;
388 }
389 match s {
391 "NaN" => return f64::NAN,
392 "Infinity" | "INF" => return f64::INFINITY,
393 "-Infinity" | "-INF" => return f64::NEG_INFINITY,
394 _ => {}
395 }
396 if let Ok(n) = s.parse::<f64>() {
398 n
399 } else {
400 f64::NAN
401 }
402}
403
404pub fn number_to_string(n: f64) -> String {
415 if n.is_nan() {
416 return "NaN".to_string();
417 }
418 if n.is_infinite() {
419 if n.is_sign_negative() {
420 return "-Infinity".to_string();
421 }
422 return "Infinity".to_string();
423 }
424 if n == 0.0 {
425 return "0".to_string();
426 }
427
428 if n.fract() == 0.0 && n.is_finite() {
430 if n.abs() < 1e16 {
432 return format!("{:.0}", n);
433 }
434 }
435
436 let s = format!("{:.15}", n);
438 let trimmed = s.trim_end_matches('0');
440 if trimmed.ends_with('.') {
442 format!("{}0", trimmed)
443 } else {
444 trimmed.to_string()
445 }
446}
447
448pub unsafe fn compare_document_order(a: *mut _xmlNode, b: *mut _xmlNode) -> Ordering {
461 if a.is_null() && b.is_null() {
462 return Ordering::Equal;
463 }
464 if a.is_null() {
465 return Ordering::Less;
466 }
467 if b.is_null() {
468 return Ordering::Greater;
469 }
470 if a == b {
471 return Ordering::Equal;
472 }
473
474 let depth_a = node_depth(a);
476 let depth_b = node_depth(b);
477
478 if depth_a < depth_b {
480 let mut n = b;
481 for _ in 0..(depth_b - depth_a) {
482 n = (*n).parent;
483 if n.is_null() {
484 break;
485 }
486 }
487 if n == a {
488 return Ordering::Less;
489 }
490 } else if depth_b < depth_a {
491 let mut n = a;
492 for _ in 0..(depth_a - depth_b) {
493 n = (*n).parent;
494 if n.is_null() {
495 break;
496 }
497 }
498 if n == b {
499 return Ordering::Greater;
500 }
501 }
502
503 let mut parent_a = a;
505 let mut parent_b = b;
506
507 let mut d_a = depth_a;
509 let mut d_b = depth_b;
510 while d_a > d_b {
511 parent_a = (*parent_a).parent;
512 d_a -= 1;
513 }
514 while d_b > d_a {
515 parent_b = (*parent_b).parent;
516 d_b -= 1;
517 }
518
519 while (*parent_a).parent != (*parent_b).parent {
521 parent_a = (*parent_a).parent;
522 parent_b = (*parent_b).parent;
523 if parent_a.is_null() || parent_b.is_null() {
524 return a.cmp(&b);
526 }
527 }
528
529 let mut n = (*parent_a).parent;
531 if n.is_null() {
532 return a.cmp(&b);
533 }
534 let mut child = (*n).children;
535 while !child.is_null() {
536 if child == parent_a {
537 return Ordering::Less;
538 }
539 if child == parent_b {
540 return Ordering::Greater;
541 }
542 child = (*child).next;
543 }
544
545 a.cmp(&b)
547}
548
549unsafe fn node_depth(node: *mut _xmlNode) -> usize {
551 let mut depth = 0;
552 let mut n = node;
553 while !(*n).parent.is_null() {
554 depth += 1;
555 n = (*n).parent;
556 }
557 depth
558}
559
560#[cfg(test)]
565mod tests {
566 use super::*;
567
568 #[test]
569 fn test_string_to_number() {
570 assert!(string_to_number("").is_nan());
571 assert!(string_to_number("NaN").is_nan());
572 assert_eq!(string_to_number("42"), 42.0);
573 assert_eq!(string_to_number("-42"), -42.0);
574 assert_eq!(string_to_number("3.14"), 3.14);
575 assert_eq!(string_to_number(" 42 "), 42.0);
576 assert!(string_to_number("true").is_nan());
577 assert!(string_to_number("false").is_nan());
578 assert_eq!(string_to_number("0"), 0.0);
579 }
580
581 #[test]
582 fn test_number_to_string() {
583 assert_eq!(number_to_string(f64::NAN), "NaN");
584 assert_eq!(number_to_string(f64::INFINITY), "Infinity");
585 assert_eq!(number_to_string(f64::NEG_INFINITY), "-Infinity");
586 assert_eq!(number_to_string(0.0), "0");
587 assert_eq!(number_to_string(-0.0), "0");
588 assert_eq!(number_to_string(42.0), "42");
589 assert_eq!(number_to_string(3.14), "3.14");
590 }
591
592 #[test]
593 fn test_value_conversions() {
594 let v = XPathValue::Number(42.0);
595 assert_eq!(v.as_number(), 42.0);
596 assert_eq!(v.as_string(), "42");
597 assert_eq!(v.as_boolean(), true);
598
599 let v = XPathValue::Number(0.0);
600 assert_eq!(v.as_boolean(), false);
601
602 let v = XPathValue::Number(f64::NAN);
603 assert_eq!(v.as_boolean(), false);
604
605 let v = XPathValue::String("hello".into());
606 assert_eq!(v.as_string(), "hello");
607 assert_eq!(v.as_boolean(), true);
608
609 let v = XPathValue::String("".into());
610 assert_eq!(v.as_boolean(), false);
611
612 let v = XPathValue::Boolean(true);
613 assert_eq!(v.as_number(), 1.0);
614 assert_eq!(v.as_string(), "true");
615
616 let v = XPathValue::Boolean(false);
617 assert_eq!(v.as_number(), 0.0);
618 assert_eq!(v.as_string(), "false");
619 }
620
621 #[test]
622 fn test_node_set() {
623 let mut ns = NodeSet::new();
624 assert!(ns.is_empty());
625 assert_eq!(ns.len(), 0);
626 }
627}