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 = crate::abi::allocator::xmlMallocImpl(
165 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_bytes_to_number(bytes: &[u8]) -> f64 {
390 let len = bytes.len();
391 let mut cur = 0usize;
392 while cur < len && matches!(bytes[cur], b' ' | b'\t' | b'\n' | b'\r') {
394 cur += 1;
395 }
396 let mut isneg = false;
397 if cur < len && bytes[cur] == b'-' {
398 isneg = true;
399 cur += 1;
400 }
401 if cur >= len || (bytes[cur] != b'.' && !bytes[cur].is_ascii_digit()) {
402 return f64::NAN;
403 }
404
405 let mut ret = 0.0f64;
406 let mut ok = false;
407 while cur < len && bytes[cur].is_ascii_digit() {
408 ret = ret * 10.0 + (bytes[cur] - b'0') as f64;
409 ok = true;
410 cur += 1;
411 }
412
413 let mut frac: i32 = 0;
414 if cur < len && bytes[cur] == b'.' {
415 cur += 1;
416 if (cur >= len || !bytes[cur].is_ascii_digit()) && !ok {
417 return f64::NAN;
418 }
419 while cur < len && bytes[cur] == b'0' {
420 frac += 1;
421 cur += 1;
422 }
423 let max = frac + 20; let mut fraction = 0.0f64;
425 while cur < len && bytes[cur].is_ascii_digit() && frac < max {
426 let v = (bytes[cur] - b'0') as f64;
427 fraction = fraction * 10.0 + v;
428 frac += 1;
429 cur += 1;
430 }
431 fraction /= 10f64.powf(frac as f64);
432 ret += fraction;
433 while cur < len && bytes[cur].is_ascii_digit() {
434 cur += 1;
435 }
436 }
437
438 let mut exponent: i32 = 0;
439 let mut is_exponent_negative = false;
440 if cur < len && (bytes[cur] == b'e' || bytes[cur] == b'E') {
441 cur += 1;
442 if cur < len && bytes[cur] == b'-' {
443 is_exponent_negative = true;
444 cur += 1;
445 } else if cur < len && bytes[cur] == b'+' {
446 cur += 1;
447 }
448 while cur < len && bytes[cur].is_ascii_digit() {
449 if exponent < 1000000 {
450 exponent = exponent * 10 + (bytes[cur] - b'0') as i32;
451 }
452 cur += 1;
453 }
454 }
455 while cur < len && matches!(bytes[cur], b' ' | b'\t' | b'\n' | b'\r') {
456 cur += 1;
457 }
458 if cur != len {
459 return f64::NAN;
460 }
461 if isneg {
462 ret = -ret;
463 }
464 if is_exponent_negative {
465 exponent = -exponent;
466 }
467 ret *= 10f64.powf(exponent as f64);
468 ret
469}
470
471pub fn string_to_number(s: &str) -> f64 {
474 string_bytes_to_number(s.as_bytes())
475}
476
477pub fn number_to_string(n: f64) -> String {
484 if n.is_nan() {
485 return "NaN".to_string();
486 }
487 if n.is_infinite() {
488 return if n > 0.0 {
489 "Infinity".to_string()
490 } else {
491 "-Infinity".to_string()
492 };
493 }
494 if n == 0.0 {
495 return "0".to_string();
497 }
498 if n > i32::MIN as f64 && n < i32::MAX as f64 && n == (n as i32) as f64 {
501 return format!("{}", n as i32);
502 }
503
504 let absolute_value = n.abs();
505 let s = if ((absolute_value > 1e9) || (absolute_value < 1e-5)) && absolute_value != 0.0 {
506 let raw = format!("{:.14e}", n);
509 let e_pos = raw.find('e').expect("exponent format contains 'e'");
510 let mantissa = &raw[..e_pos];
511 let exponent = &raw[e_pos + 1..];
512 let mut mantissa = mantissa.to_string();
513 while mantissa.ends_with('0') {
514 mantissa.pop();
515 }
516 if mantissa.ends_with('.') {
517 mantissa.pop();
518 }
519 let (sign, digits) = if let Some(rest) = exponent.strip_prefix('-') {
522 ("-", rest)
523 } else {
524 ("+", exponent)
525 };
526 let digits = if digits.len() < 2 {
527 format!("0{}", digits)
528 } else {
529 digits.to_string()
530 };
531 format!("{}e{}{}", mantissa, sign, digits)
532 } else {
533 let integer_place = absolute_value.log10() as i32;
535 let fraction_place = if integer_place > 0 {
536 15 - integer_place - 1
537 } else {
538 15 - integer_place
539 };
540 let mut s = format!("{:.*}", fraction_place as usize, n);
541 if s.contains('.') {
543 while s.ends_with('0') {
544 s.pop();
545 }
546 if s.ends_with('.') {
547 s.pop();
548 }
549 }
550 s
551 };
552 if s == "-0" {
553 return "0".to_string();
554 }
555 s
556}
557
558pub unsafe fn compare_document_order(a: *mut _xmlNode, b: *mut _xmlNode) -> Ordering {
571 if a.is_null() && b.is_null() {
572 return Ordering::Equal;
573 }
574 if a.is_null() {
575 return Ordering::Less;
576 }
577 if b.is_null() {
578 return Ordering::Greater;
579 }
580 if a == b {
581 return Ordering::Equal;
582 }
583
584 let depth_a = node_depth(a);
586 let depth_b = node_depth(b);
587
588 if depth_a < depth_b {
590 let mut n = b;
591 for _ in 0..(depth_b - depth_a) {
592 n = (*n).parent;
593 if n.is_null() {
594 break;
595 }
596 }
597 if n == a {
598 return Ordering::Less;
599 }
600 } else if depth_b < depth_a {
601 let mut n = a;
602 for _ in 0..(depth_a - depth_b) {
603 n = (*n).parent;
604 if n.is_null() {
605 break;
606 }
607 }
608 if n == b {
609 return Ordering::Greater;
610 }
611 }
612
613 let mut parent_a = a;
615 let mut parent_b = b;
616
617 let mut d_a = depth_a;
619 let mut d_b = depth_b;
620 while d_a > d_b {
621 parent_a = (*parent_a).parent;
622 d_a -= 1;
623 }
624 while d_b > d_a {
625 parent_b = (*parent_b).parent;
626 d_b -= 1;
627 }
628
629 while (*parent_a).parent != (*parent_b).parent {
631 parent_a = (*parent_a).parent;
632 parent_b = (*parent_b).parent;
633 if parent_a.is_null() || parent_b.is_null() {
634 return a.cmp(&b);
636 }
637 }
638
639 let mut n = (*parent_a).parent;
641 if n.is_null() {
642 return a.cmp(&b);
643 }
644 let mut child = (*n).children;
645 while !child.is_null() {
646 if child == parent_a {
647 return Ordering::Less;
648 }
649 if child == parent_b {
650 return Ordering::Greater;
651 }
652 child = (*child).next;
653 }
654
655 a.cmp(&b)
657}
658
659unsafe fn node_depth(node: *mut _xmlNode) -> usize {
661 let mut depth = 0;
662 let mut n = node;
663 while !(*n).parent.is_null() {
664 depth += 1;
665 n = (*n).parent;
666 }
667 depth
668}
669
670#[cfg(test)]
675mod tests {
676 use super::*;
677
678 #[test]
679 fn test_string_to_number() {
680 assert!(string_to_number("").is_nan());
681 assert!(string_to_number("NaN").is_nan());
682 assert_eq!(string_to_number("42"), 42.0);
683 assert_eq!(string_to_number("-42"), -42.0);
684 assert_eq!(string_to_number("3.14"), 3.14);
685 assert_eq!(string_to_number(" 42 "), 42.0);
686 assert!(string_to_number("true").is_nan());
687 assert!(string_to_number("false").is_nan());
688 assert_eq!(string_to_number("0"), 0.0);
689 }
690
691 #[test]
692 fn test_number_to_string() {
693 assert_eq!(number_to_string(f64::NAN), "NaN");
694 assert_eq!(number_to_string(f64::INFINITY), "Infinity");
695 assert_eq!(number_to_string(f64::NEG_INFINITY), "-Infinity");
696 assert_eq!(number_to_string(0.0), "0");
697 assert_eq!(number_to_string(-0.0), "0");
698 assert_eq!(number_to_string(42.0), "42");
699 assert_eq!(number_to_string(3.14), "3.14");
700 }
701
702 #[test]
703 fn test_value_conversions() {
704 let v = XPathValue::Number(42.0);
705 assert_eq!(v.as_number(), 42.0);
706 assert_eq!(v.as_string(), "42");
707 assert_eq!(v.as_boolean(), true);
708
709 let v = XPathValue::Number(0.0);
710 assert_eq!(v.as_boolean(), false);
711
712 let v = XPathValue::Number(f64::NAN);
713 assert_eq!(v.as_boolean(), false);
714
715 let v = XPathValue::String("hello".into());
716 assert_eq!(v.as_string(), "hello");
717 assert_eq!(v.as_boolean(), true);
718
719 let v = XPathValue::String("".into());
720 assert_eq!(v.as_boolean(), false);
721
722 let v = XPathValue::Boolean(true);
723 assert_eq!(v.as_number(), 1.0);
724 assert_eq!(v.as_string(), "true");
725
726 let v = XPathValue::Boolean(false);
727 assert_eq!(v.as_number(), 0.0);
728 assert_eq!(v.as_string(), "false");
729 }
730
731 #[test]
732 fn test_node_set() {
733 let mut ns = NodeSet::new();
734 assert!(ns.is_empty());
735 assert_eq!(ns.len(), 0);
736 }
737}