1#![forbid(unsafe_code)]
4
5use rusty_xml_tree::{NodeId, NodeKind, XmlDoc};
6use std::cmp::Ordering;
7
8pub const XPATH_UNDEFINED: i32 = 0;
10pub const XPATH_NODESET: i32 = 1;
11pub const XPATH_BOOLEAN: i32 = 2;
12pub const XPATH_NUMBER: i32 = 3;
13pub const XPATH_STRING: i32 = 4;
14
15#[derive(Clone, Debug)]
16pub enum XPathObject {
17 Undefined,
18 NodeSet(Vec<NodeId>),
19 Boolean(bool),
20 Number(f64),
21 String(String),
22}
23
24impl XPathObject {
25 pub fn object_type(&self) -> i32 {
26 match self {
27 XPathObject::Undefined => XPATH_UNDEFINED,
28 XPathObject::NodeSet(_) => XPATH_NODESET,
29 XPathObject::Boolean(_) => XPATH_BOOLEAN,
30 XPathObject::Number(_) => XPATH_NUMBER,
31 XPathObject::String(_) => XPATH_STRING,
32 }
33 }
34}
35
36pub struct XmlXPathContext<'a> {
37 pub doc: &'a XmlDoc,
38 pub node: NodeId,
39 pub position: usize,
40 pub size: usize,
41 ns: Vec<(String, String)>,
42}
43
44impl<'a> XmlXPathContext<'a> {
45 #[doc(alias = "xmlXPathNewContext")]
47 pub fn xml_xpath_new_context(doc: &'a XmlDoc) -> Self {
48 Self {
49 node: doc.xml_doc_get_root_element().unwrap_or(NodeId::DOCUMENT),
50 doc,
51 position: 1,
52 size: 1,
53 ns: Vec::new(),
54 }
55 }
56
57 #[doc(alias = "xmlXPathSetContextNode")]
59 pub fn xml_xpath_set_context_node(&mut self, node: NodeId) {
60 self.node = node;
61 }
62
63 pub fn register_ns(&mut self, prefix: &str, href: &str) {
64 self.ns.push((prefix.to_string(), href.to_string()));
65 }
66}
67
68#[doc(alias = "xmlXPathIsNaN")]
70pub fn xml_xpath_is_nan(v: f64) -> bool {
71 v.is_nan()
72}
73
74#[doc(alias = "xmlXPathIsInf")]
76pub fn xml_xpath_is_inf(v: f64) -> i32 {
77 if v.is_infinite() {
78 if v.is_sign_positive() { 1 } else { -1 }
79 } else {
80 0
81 }
82}
83
84#[doc(alias = "xmlXPathOrderDocElems")]
86pub fn xml_xpath_order_doc_elems(doc: &XmlDoc) -> Vec<NodeId> {
87 let mut out = Vec::new();
88 fn walk(doc: &XmlDoc, id: NodeId, out: &mut Vec<NodeId>) {
89 out.push(id);
90 let mut a = doc.first_attr(id);
91 while let Some(x) = a {
92 out.push(x);
93 a = doc.next_sibling(x);
94 }
95 let mut c = doc.first_child(id);
96 while let Some(x) = c {
97 walk(doc, x, out);
98 c = doc.next_sibling(x);
99 }
100 }
101 walk(doc, NodeId::DOCUMENT, &mut out);
102 out
103}
104
105#[doc(alias = "xmlXPathCmpNodes")]
107pub fn xml_xpath_cmp_nodes(doc: &XmlDoc, a: NodeId, b: NodeId) -> i32 {
108 let order = xml_xpath_order_doc_elems(doc);
109 let ia = order.iter().position(|n| *n == a);
110 let ib = order.iter().position(|n| *n == b);
111 match (ia, ib) {
112 (Some(x), Some(y)) => x.cmp(&y) as i32,
113 _ => a.0.cmp(&b.0) as i32,
114 }
115}
116
117#[doc(alias = "xmlXPathEval")]
119pub fn xml_xpath_eval(expr: &str, ctx: &XmlXPathContext<'_>) -> Result<XPathObject, String> {
120 let mut p = Parser {
121 src: expr.trim(),
122 pos: 0,
123 depth: 0,
124 };
125 let ast = p.parse_expr()?;
126 p.skip_ws();
127 if p.pos < p.src.len() && !p.src[p.pos..].chars().all(|c| c.is_whitespace()) {
128 return Err(format!("trailing junk in XPath: {}", &p.src[p.pos..]));
129 }
130 eval(&ast, ctx)
131}
132
133#[doc(alias = "xmlXPathCastToBoolean")]
135pub fn xml_xpath_cast_to_boolean(o: &XPathObject) -> bool {
136 match o {
137 XPathObject::Boolean(b) => *b,
138 XPathObject::Number(n) => *n != 0.0 && !n.is_nan(),
139 XPathObject::String(s) => !s.is_empty(),
140 XPathObject::NodeSet(v) => !v.is_empty(),
141 XPathObject::Undefined => false,
142 }
143}
144
145#[doc(alias = "xmlXPathCastToNumber")]
147pub fn xml_xpath_cast_to_number(o: &XPathObject, ctx: &XmlXPathContext<'_>) -> f64 {
148 match o {
149 XPathObject::Number(n) => *n,
150 XPathObject::Boolean(b) => {
151 if *b { 1.0 } else { 0.0 }
152 }
153 XPathObject::String(s) => xpath_number(s),
154 XPathObject::NodeSet(v) => xml_xpath_cast_to_number(&XPathObject::String(string_val(ctx, v)), ctx),
155 XPathObject::Undefined => f64::NAN,
156 }
157}
158
159#[doc(alias = "xmlXPathCastToString")]
161pub fn xml_xpath_cast_to_string(o: &XPathObject, ctx: &XmlXPathContext<'_>) -> String {
162 match o {
163 XPathObject::String(s) => s.clone(),
164 XPathObject::Boolean(b) => if *b { "true".into() } else { "false".into() },
165 XPathObject::Number(n) => number_to_string(*n),
166 XPathObject::NodeSet(v) => string_val(ctx, v),
167 XPathObject::Undefined => String::new(),
168 }
169}
170
171fn number_to_string(n: f64) -> String {
172 if n.is_nan() {
173 return "NaN".into();
174 }
175 if n.is_infinite() {
176 return if n.is_sign_positive() {
177 "Infinity".into()
178 } else {
179 "-Infinity".into()
180 };
181 }
182 if n == 0.0 {
183 return "0".into();
184 }
185 if n > i32::MIN as f64 && n < i32::MAX as f64 && n == (n as i32) as f64 {
186 return format!("{}", n as i32);
187 }
188 let abs = n.abs();
189 if abs > 1e9 || abs < 1e-5 {
190 let s = format!("{n:.14e}");
191 let Some(idx) = s.find('e') else { return s };
192 let mant = &s[..idx];
193 let es = &s[idx..];
194 let mut mant = mant.to_string();
195 if mant.contains('.') {
196 while mant.ends_with('0') {
197 mant.pop();
198 }
199 if mant.ends_with('.') {
200 mant.pop();
201 }
202 }
203 let es = if es.starts_with("e-") || es.starts_with("e+") {
204 es.to_string()
205 } else {
206 format!("e+{}", &es[1..])
207 };
208 format!("{mant}{es}")
209 } else {
210 let mut s = format!("{n}");
211 if let Some(rest) = s.strip_prefix("-") {
212 if rest.starts_with('.') {
213 s = format!("-0{rest}");
214 }
215 } else if s.starts_with('.') {
216 s = format!("0{s}");
217 }
218 s
219 }
220}
221
222fn dump_number(n: f64) -> String {
223 if n.is_nan() {
224 return "NaN".into();
225 }
226 if n.is_infinite() {
227 return if n.is_sign_positive() {
228 "Infinity".into()
229 } else {
230 "-Infinity".into()
231 };
232 }
233 if n == 0.0 {
234 return "0".into();
235 }
236 let exp = n.abs().log10().floor() as i32;
238 if exp < -4 || exp >= 6 {
239 let s = format!("{n:.5e}");
240 let Some(idx) = s.find('e') else { return s };
241 let mant = &s[..idx];
242 let es = &s[idx..];
243 let mut mant = mant.to_string();
244 if mant.contains('.') {
245 while mant.ends_with('0') {
246 mant.pop();
247 }
248 if mant.ends_with('.') {
249 mant.pop();
250 }
251 }
252 let es = if es.starts_with("e-") || es.starts_with("e+") {
253 es.to_string()
254 } else {
255 format!("e+{}", &es[1..])
256 };
257 format!("{mant}{es}")
258 } else {
259 format!("{n}")
260 }
261}
262
263fn xpath_number(s: &str) -> f64 {
264 let t = s.trim();
265 if t.is_empty() {
266 return f64::NAN;
267 }
268 t.parse::<f64>().unwrap_or(f64::NAN)
269}
270
271fn string_val(ctx: &XmlXPathContext<'_>, nodes: &[NodeId]) -> String {
272 let Some(&id) = nodes.first() else { return String::new() };
273 ctx.doc.xml_node_get_content(id)
274}
275
276pub fn xml_xpath_debug_dump(obj: &XPathObject, ctx: &XmlXPathContext<'_>) -> String {
278 match obj {
279 XPathObject::Undefined => "Object is empty (NULL)\n".into(),
280 XPathObject::Number(n) => format!("Object is a number : {}\n", dump_number(*n)),
281 XPathObject::Boolean(b) => format!(
282 "Object is a Boolean : {}\n",
283 if *b { "true" } else { "false" }
284 ),
285 XPathObject::String(s) => format!("Object is a string : {}\n", debug_string(s)),
286 XPathObject::NodeSet(v) => {
287 let mut s = format!("Object is a Node Set :\nSet contains {} nodes:\n", v.len());
288 for (i, id) in v.iter().enumerate() {
289 s.push_str(&format!("{}", i + 1));
290 dump_one(ctx.doc, *id, 1, &mut s);
291 }
292 s
293 }
294 }
295}
296
297fn debug_string(s: &str) -> String {
298 let mut out = String::new();
299 for (i, c) in s.chars().enumerate() {
300 if i >= 40 {
301 out.push_str("...");
302 break;
303 }
304 if c.is_whitespace() {
305 out.push(' ');
306 } else if (c as u32) >= 0x80 {
307 out.push_str(&format!("#{:X}", c as u32));
308 } else {
309 out.push(c);
310 }
311 }
312 out
313}
314
315fn spaces(depth: i32) -> String {
316 " ".repeat(depth.max(0) as usize)
317}
318
319fn dump_one(doc: &XmlDoc, id: NodeId, depth: i32, out: &mut String) {
320 match doc.kind(id) {
321 NodeKind::Document | NodeKind::HtmlDocument => {
322 out.push_str(&spaces(depth));
323 out.push_str(" /\n");
324 }
325 NodeKind::Element => {
326 out.push_str(&spaces(depth));
327 out.push_str("ELEMENT ");
328 out.push_str(&doc.qname(id));
329 out.push('\n');
330 for (pre, href) in doc.ns_defs(id) {
331 out.push_str(&spaces(depth + 1));
332 match pre {
333 Some(p) => out.push_str(&format!("namespace {p} href={href}\n")),
334 None => out.push_str(&format!("default namespace href={href}\n")),
335 }
336 }
337 let mut a = doc.first_attr(id);
338 while let Some(x) = a {
339 dump_attr(doc, x, depth + 1, out);
340 a = doc.next_sibling(x);
341 }
342 }
343 NodeKind::Attribute => dump_attr(doc, id, depth, out),
344 NodeKind::Text => {
345 out.push_str(&spaces(depth));
346 out.push_str("TEXT\n");
347 out.push_str(&spaces(depth + 1));
348 out.push_str("content=");
349 out.push_str(&debug_string(doc.content(id)));
350 out.push('\n');
351 }
352 NodeKind::CData => {
353 out.push_str(&spaces(depth));
354 out.push_str("CDATA_SECTION\n");
355 }
356 NodeKind::Comment => {
357 out.push_str(&spaces(depth));
358 out.push_str("COMMENT\n");
359 }
360 NodeKind::Pi => {
361 out.push_str(&spaces(depth));
362 out.push_str(&format!("PI {}\n", doc.name(id)));
363 }
364 _ => {
365 out.push_str(&spaces(depth));
366 out.push_str(&format!("{:?}\n", doc.kind(id)));
367 }
368 }
369}
370
371fn dump_attr(doc: &XmlDoc, id: NodeId, depth: i32, out: &mut String) {
372 out.push_str(&spaces(depth));
373 out.push_str("ATTRIBUTE ");
374 out.push_str(doc.name(id));
375 out.push('\n');
376 out.push_str(&spaces(depth + 1));
377 out.push_str("TEXT\n");
378 out.push_str(&spaces(depth + 2));
379 out.push_str("content=");
380 out.push_str(&debug_string(doc.content(id)));
381 out.push('\n');
382}
383
384pub fn xml_xpath_print_lint(obj: &XPathObject) -> Option<String> {
386 match obj {
387 XPathObject::Undefined => Some(String::new()),
388 XPathObject::Number(n) => Some(format!("{}\n", dump_number(*n))),
389 XPathObject::Boolean(b) => Some(format!("{}\n", if *b { "true" } else { "false" })),
390 XPathObject::String(s) => Some(format!("{s}\n")),
391 XPathObject::NodeSet(_) => None,
392 }
393}
394#[doc(alias = "xmlXPathCompile")]
395pub fn xml_xpath_compile(expr: &str) -> Result<String, String> {
396 let mut p = Parser {
397 src: expr.trim(),
398 pos: 0,
399 depth: 0,
400 };
401 let _ = p.parse_expr()?;
402 Ok(expr.to_string())
403}
404
405#[doc(alias = "xmlXPathCompiledEval")]
407pub fn xml_xpath_compiled_eval(
408 expr: &str,
409 ctx: &XmlXPathContext<'_>,
410) -> Result<XPathObject, String> {
411 xml_xpath_eval(expr, ctx)
412}
413
414#[derive(Clone, Debug)]
417enum Expr {
418 Or(Box<Expr>, Box<Expr>),
419 And(Box<Expr>, Box<Expr>),
420 Eq(Box<Expr>, Box<Expr>, bool),
421 Rel(Box<Expr>, Box<Expr>, Ordering, bool),
422 Add(Box<Expr>, Box<Expr>, bool),
423 Mul(Box<Expr>, Box<Expr>, char),
424 Neg(Box<Expr>),
425 Union(Box<Expr>, Box<Expr>),
426 Path(PathExpr),
427 Steps { base: Box<Expr>, steps: Vec<Step> },
428 Filter(Box<Expr>, Vec<Expr>),
429 Literal(String),
430 Number(f64),
431 Var(String),
432 Fun { name: String, args: Vec<Expr> },
433}
434
435#[derive(Clone, Debug)]
436struct PathExpr {
437 abs: bool,
438 steps: Vec<Step>,
439}
440
441#[derive(Clone, Debug)]
442struct Step {
443 axis: Axis,
444 test: NodeTest,
445 preds: Vec<Expr>,
446}
447
448#[derive(Clone, Copy, Debug, PartialEq, Eq)]
449enum Axis {
450 Child,
451 Descendant,
452 Parent,
453 Ancestor,
454 FollowingSibling,
455 PrecedingSibling,
456 Following,
457 Preceding,
458 Attribute,
459 Namespace,
460 SelfAxis,
461 DescendantOrSelf,
462 AncestorOrSelf,
463}
464
465#[derive(Clone, Debug)]
466enum NodeTest {
467 Star,
468 Name(String, Option<String>),
469 Node,
470 Text,
471 Comment,
472 Pi(Option<String>),
473}
474
475const MAX_XPATH_DEPTH: u32 = 64;
489
490struct Parser<'a> {
491 src: &'a str,
492 pos: usize,
493 depth: u32,
494}
495
496impl<'a> Parser<'a> {
497 fn enter(&mut self) -> Result<(), String> {
498 self.depth += 1;
499 if self.depth > MAX_XPATH_DEPTH {
500 return Err("expression nested too deeply".into());
501 }
502 Ok(())
503 }
504 fn leave(&mut self) {
505 self.depth = self.depth.saturating_sub(1);
506 }
507 fn skip_ws(&mut self) {
508 while let Some(c) = self.src[self.pos..].chars().next() {
509 if c.is_whitespace() {
510 self.pos += c.len_utf8();
511 } else {
512 break;
513 }
514 }
515 }
516 fn peek(&self) -> Option<char> {
517 self.src[self.pos..].chars().next()
518 }
519 fn starts(&self, s: &str) -> bool {
520 self.src[self.pos..].starts_with(s)
521 }
522 fn bump(&mut self, n: usize) {
523 self.pos += n;
524 }
525 fn parse_expr(&mut self) -> Result<Expr, String> {
526 self.enter()?;
527 let r = self.parse_or();
528 self.leave();
529 r
530 }
531 fn parse_or(&mut self) -> Result<Expr, String> {
532 let mut e = self.parse_and()?;
533 loop {
534 self.skip_ws();
535 if self.keyword("or") {
536 let r = self.parse_and()?;
537 e = Expr::Or(Box::new(e), Box::new(r));
538 } else {
539 break;
540 }
541 }
542 Ok(e)
543 }
544 fn parse_and(&mut self) -> Result<Expr, String> {
545 let mut e = self.parse_eq()?;
546 loop {
547 self.skip_ws();
548 if self.keyword("and") {
549 let r = self.parse_eq()?;
550 e = Expr::And(Box::new(e), Box::new(r));
551 } else {
552 break;
553 }
554 }
555 Ok(e)
556 }
557 fn parse_eq(&mut self) -> Result<Expr, String> {
558 let mut e = self.parse_rel()?;
559 loop {
560 self.skip_ws();
561 if self.starts("=") {
562 self.bump(1);
563 let r = self.parse_rel()?;
564 e = Expr::Eq(Box::new(e), Box::new(r), true);
565 } else if self.starts("!=") {
566 self.bump(2);
567 let r = self.parse_rel()?;
568 e = Expr::Eq(Box::new(e), Box::new(r), false);
569 } else {
570 break;
571 }
572 }
573 Ok(e)
574 }
575 fn parse_rel(&mut self) -> Result<Expr, String> {
576 let mut e = self.parse_add()?;
577 loop {
578 self.skip_ws();
579 if self.starts("<=") {
580 self.bump(2);
581 let r = self.parse_add()?;
582 e = Expr::Rel(Box::new(e), Box::new(r), Ordering::Less, true);
583 } else if self.starts(">=") {
584 self.bump(2);
585 let r = self.parse_add()?;
586 e = Expr::Rel(Box::new(e), Box::new(r), Ordering::Greater, true);
587 } else if self.starts("<") {
588 self.bump(1);
589 let r = self.parse_add()?;
590 e = Expr::Rel(Box::new(e), Box::new(r), Ordering::Less, false);
591 } else if self.starts(">") {
592 self.bump(1);
593 let r = self.parse_add()?;
594 e = Expr::Rel(Box::new(e), Box::new(r), Ordering::Greater, false);
595 } else {
596 break;
597 }
598 }
599 Ok(e)
600 }
601 fn parse_add(&mut self) -> Result<Expr, String> {
602 let mut e = self.parse_mul()?;
603 loop {
604 self.skip_ws();
605 if self.starts("+") {
606 self.bump(1);
607 let r = self.parse_mul()?;
608 e = Expr::Add(Box::new(e), Box::new(r), true);
609 } else if self.starts("-") && !self.is_name_start_after_minus() {
610 self.bump(1);
611 let r = self.parse_mul()?;
612 e = Expr::Add(Box::new(e), Box::new(r), false);
613 } else {
614 break;
615 }
616 }
617 Ok(e)
618 }
619 fn is_name_start_after_minus(&self) -> bool {
620 false
621 }
622 fn parse_mul(&mut self) -> Result<Expr, String> {
623 let mut e = self.parse_unary()?;
624 loop {
625 self.skip_ws();
626 if self.starts("*") && !self.looks_like_nametest_star() {
627 self.bump(1);
628 let r = self.parse_unary()?;
629 e = Expr::Mul(Box::new(e), Box::new(r), '*');
630 } else if self.keyword("div") {
631 let r = self.parse_unary()?;
632 e = Expr::Mul(Box::new(e), Box::new(r), 'd');
633 } else if self.keyword("mod") {
634 let r = self.parse_unary()?;
635 e = Expr::Mul(Box::new(e), Box::new(r), 'm');
636 } else {
637 break;
638 }
639 }
640 Ok(e)
641 }
642 fn looks_like_nametest_star(&self) -> bool {
643 false
644 }
645 fn parse_unary(&mut self) -> Result<Expr, String> {
646 self.skip_ws();
647 if self.starts("-") {
648 self.bump(1);
649 self.enter()?;
650 let inner = self.parse_unary();
651 self.leave();
652 Ok(Expr::Neg(Box::new(inner?)))
653 } else {
654 self.parse_union()
655 }
656 }
657 fn parse_union(&mut self) -> Result<Expr, String> {
658 let mut e = self.parse_path()?;
659 let mut arms = 0u32;
662 loop {
663 self.skip_ws();
664 if self.starts("|") {
665 self.bump(1);
666 arms += 1;
667 if arms > MAX_XPATH_DEPTH {
668 return Err("expression nested too deeply".into());
669 }
670 let r = self.parse_path()?;
671 e = Expr::Union(Box::new(e), Box::new(r));
672 } else {
673 break;
674 }
675 }
676 Ok(e)
677 }
678 fn parse_path(&mut self) -> Result<Expr, String> {
679 self.skip_ws();
680 if self.looks_like_primary() {
681 return self.parse_filter_or_primary();
682 }
683 let abs = if self.starts("//") {
684 self.bump(2);
685 let mut steps = vec![Step {
686 axis: Axis::DescendantOrSelf,
687 test: NodeTest::Node,
688 preds: vec![],
689 }];
690 steps.extend(self.parse_relative()?);
691 return Ok(Expr::Path(PathExpr { abs: true, steps }));
692 } else if self.starts("/") {
693 self.bump(1);
694 true
695 } else {
696 false
697 };
698 let steps = self.parse_relative()?;
699 if abs || !steps.is_empty() {
700 Ok(Expr::Path(PathExpr { abs, steps }))
701 } else {
702 self.parse_filter_or_primary()
703 }
704 }
705 fn looks_like_primary(&self) -> bool {
706 let s = self.src[self.pos..].trim_start();
707 match s.chars().next() {
708 Some('(' | '$' | '\'' | '"') => true,
709 Some(d) if d.is_ascii_digit() => true,
710 Some('.') => s[1..].chars().next().map(|x| x.is_ascii_digit()).unwrap_or(false),
711 Some(c) if c.is_ascii_alphabetic() || c == '_' => {
712 let i = s
713 .find(|ch: char| !(ch.is_ascii_alphanumeric() || "-._:".contains(ch)))
714 .unwrap_or(s.len());
715 s[i..].trim_start().starts_with('(')
716 }
717 _ => false,
718 }
719 }
720 fn parse_relative(&mut self) -> Result<Vec<Step>, String> {
721 let mut steps = Vec::new();
722 self.skip_ws();
723 if self.peek().is_none() || matches!(self.peek(), Some(')' | ',' | '|' | ']')) {
724 return Ok(steps);
725 }
726 if self.starts("/") && !self.starts("//") {
727 return Ok(steps);
728 }
729 steps.push(self.parse_step()?);
730 loop {
731 self.skip_ws();
732 if self.starts("//") {
733 self.bump(2);
734 steps.push(Step {
735 axis: Axis::DescendantOrSelf,
736 test: NodeTest::Node,
737 preds: vec![],
738 });
739 steps.push(self.parse_step()?);
740 } else if self.starts("/") {
741 self.bump(1);
742 steps.push(self.parse_step()?);
743 } else {
744 break;
745 }
746 }
747 Ok(steps)
748 }
749 fn parse_step(&mut self) -> Result<Step, String> {
750 self.skip_ws();
751 if self.starts("..") {
752 self.bump(2);
753 return Ok(Step {
754 axis: Axis::Parent,
755 test: NodeTest::Node,
756 preds: self.parse_preds()?,
757 });
758 }
759 if self.starts(".") {
760 self.bump(1);
761 return Ok(Step {
762 axis: Axis::SelfAxis,
763 test: NodeTest::Node,
764 preds: self.parse_preds()?,
765 });
766 }
767 if self.starts("@") {
768 self.bump(1);
769 let test = self.parse_node_test()?;
770 return Ok(Step {
771 axis: Axis::Attribute,
772 test,
773 preds: self.parse_preds()?,
774 });
775 }
776 let save = self.pos;
777 if let Some(axis) = self.try_axis() {
778 let test = self.parse_node_test()?;
779 return Ok(Step {
780 axis,
781 test,
782 preds: self.parse_preds()?,
783 });
784 }
785 self.pos = save;
786 let test = self.parse_node_test()?;
787 Ok(Step {
788 axis: Axis::Child,
789 test,
790 preds: self.parse_preds()?,
791 })
792 }
793 fn try_axis(&mut self) -> Option<Axis> {
794 self.skip_ws();
795 let names = [
796 ("descendant-or-self", Axis::DescendantOrSelf),
797 ("following-sibling", Axis::FollowingSibling),
798 ("preceding-sibling", Axis::PrecedingSibling),
799 ("ancestor-or-self", Axis::AncestorOrSelf),
800 ("descendant", Axis::Descendant),
801 ("attribute", Axis::Attribute),
802 ("following", Axis::Following),
803 ("namespace", Axis::Namespace),
804 ("preceding", Axis::Preceding),
805 ("ancestor", Axis::Ancestor),
806 ("parent", Axis::Parent),
807 ("child", Axis::Child),
808 ("self", Axis::SelfAxis),
809 ];
810 for (n, ax) in names {
811 if self.src[self.pos..].starts_with(n) {
812 let after = self.pos + n.len();
813 if self.src[after..].starts_with("::") {
814 self.pos = after + 2;
815 return Some(ax);
816 }
817 }
818 }
819 None
820 }
821 fn parse_node_test(&mut self) -> Result<NodeTest, String> {
822 self.skip_ws();
823 if self.starts("*") {
824 self.bump(1);
825 return Ok(NodeTest::Star);
826 }
827 if self.fn_test("node") {
828 return Ok(NodeTest::Node);
829 }
830 if self.fn_test("text") {
831 return Ok(NodeTest::Text);
832 }
833 if self.fn_test("comment") {
834 return Ok(NodeTest::Comment);
835 }
836 if self.src[self.pos..].starts_with("processing-instruction") {
837 self.pos += "processing-instruction".len();
838 self.skip_ws();
839 if !self.starts("(") {
840 return Err("expected (".into());
841 }
842 self.bump(1);
843 self.skip_ws();
844 let lit = if self.starts("'") || self.starts("\"") {
845 Some(self.parse_literal_raw()?)
846 } else {
847 None
848 };
849 self.skip_ws();
850 if self.starts(")") {
851 self.bump(1);
852 }
853 return Ok(NodeTest::Pi(lit));
854 }
855 let name = self.parse_qname()?;
856 let (prefix, local) = split_qname(&name);
857 Ok(NodeTest::Name(local, prefix))
858 }
859 fn fn_test(&mut self, n: &str) -> bool {
860 if self.src[self.pos..].starts_with(n) {
861 let after = self.pos + n.len();
862 let rest = &self.src[after..];
863 let trimmed = rest.trim_start();
864 if trimmed.starts_with("()") || trimmed.starts_with('(') {
865 if let Some(idx) = rest.find(')') {
867 self.pos = after + idx + 1;
868 return true;
869 }
870 }
871 }
872 false
873 }
874 fn parse_preds(&mut self) -> Result<Vec<Expr>, String> {
875 let mut v = Vec::new();
876 loop {
877 self.skip_ws();
878 if self.starts("[") {
879 self.bump(1);
880 v.push(self.parse_expr()?);
881 self.skip_ws();
882 if self.starts("]") {
883 self.bump(1);
884 } else {
885 return Err("expected ]".into());
886 }
887 } else {
888 break;
889 }
890 }
891 Ok(v)
892 }
893 fn parse_filter_or_primary(&mut self) -> Result<Expr, String> {
894 let mut e = self.parse_primary()?;
895 let preds = self.parse_preds()?;
896 if !preds.is_empty() {
897 e = Expr::Filter(Box::new(e), preds);
898 }
899 self.skip_ws();
900 if self.starts("/") {
901 let mut steps = Vec::new();
902 if self.starts("//") {
903 self.bump(2);
904 steps.push(Step {
905 axis: Axis::DescendantOrSelf,
906 test: NodeTest::Node,
907 preds: vec![],
908 });
909 } else {
910 self.bump(1);
911 }
912 steps.extend(self.parse_relative()?);
913 return Ok(Expr::Steps {
914 base: Box::new(e),
915 steps,
916 });
917 }
918 Ok(e)
919 }
920 fn parse_primary(&mut self) -> Result<Expr, String> {
921 self.skip_ws();
922 if self.starts("(") {
923 self.bump(1);
924 let e = self.parse_expr()?;
925 self.skip_ws();
926 if self.starts(")") {
927 self.bump(1);
928 }
929 return Ok(e);
930 }
931 if self.starts("$") {
932 self.bump(1);
933 return Ok(Expr::Var(self.parse_qname()?));
934 }
935 if self.starts("'") || self.starts("\"") {
936 return Ok(Expr::Literal(self.parse_literal_raw()?));
937 }
938 if self.peek().map(|c| c.is_ascii_digit() || c == '.').unwrap_or(false) {
939 return Ok(Expr::Number(self.parse_number()?));
940 }
941 let name = self.parse_qname()?;
942 self.skip_ws();
943 if self.starts("(") {
944 self.bump(1);
945 let mut args = Vec::new();
946 self.skip_ws();
947 if !self.starts(")") {
948 args.push(self.parse_expr()?);
949 loop {
950 self.skip_ws();
951 if self.starts(",") {
952 self.bump(1);
953 args.push(self.parse_expr()?);
954 } else {
955 break;
956 }
957 }
958 }
959 self.skip_ws();
960 if self.starts(")") {
961 self.bump(1);
962 }
963 return Ok(Expr::Fun { name, args });
964 }
965 let (prefix, local) = split_qname(&name);
967 Ok(Expr::Path(PathExpr {
968 abs: false,
969 steps: vec![Step {
970 axis: Axis::Child,
971 test: NodeTest::Name(local, prefix),
972 preds: vec![],
973 }],
974 }))
975 }
976 fn parse_literal_raw(&mut self) -> Result<String, String> {
977 let q = self.peek().ok_or("literal")?;
978 self.bump(1);
979 if let Some(end) = self.src[self.pos..].find(q) {
980 let s = self.src[self.pos..self.pos + end].to_string();
981 self.pos += end + 1;
982 Ok(s)
983 } else {
984 Err("unterminated string".into())
985 }
986 }
987 fn parse_number(&mut self) -> Result<f64, String> {
988 let start = self.pos;
989 while self.peek().map(|c| c.is_ascii_digit()).unwrap_or(false) {
990 self.bump(1);
991 }
992 if self.peek() == Some('.') {
993 self.bump(1);
994 while self.peek().map(|c| c.is_ascii_digit()).unwrap_or(false) {
995 self.bump(1);
996 }
997 }
998 if matches!(self.peek(), Some('e' | 'E')) {
999 self.bump(1);
1000 if matches!(self.peek(), Some('+' | '-')) {
1001 self.bump(1);
1002 }
1003 while self.peek().map(|c| c.is_ascii_digit()).unwrap_or(false) {
1004 self.bump(1);
1005 }
1006 }
1007 let s = &self.src[start..self.pos];
1008 Ok(s.parse::<f64>().unwrap_or(f64::NAN))
1009 }
1010 fn parse_qname(&mut self) -> Result<String, String> {
1011 self.skip_ws();
1012 let start = self.pos;
1013 let first = self.peek().ok_or_else(|| format!("expected name at {}", &self.src[self.pos..]))?;
1014 if !(first.is_ascii_alphabetic() || first == '_' || first == ':') {
1015 return Err(format!("expected name at {}", &self.src[self.pos..]));
1016 }
1017 self.bump(first.len_utf8());
1018 while let Some(c) = self.peek() {
1019 if c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.' || c == ':' {
1020 self.bump(c.len_utf8());
1021 } else {
1022 break;
1023 }
1024 }
1025 Ok(self.src[start..self.pos].to_string())
1026 }
1027 fn keyword(&mut self, kw: &str) -> bool {
1028 self.skip_ws();
1029 if self.src[self.pos..].starts_with(kw) {
1030 let after = self.pos + kw.len();
1031 let next = self.src[after..].chars().next();
1032 if next.map(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-').unwrap_or(false) {
1033 return false;
1034 }
1035 self.pos = after;
1036 true
1037 } else {
1038 false
1039 }
1040 }
1041}
1042
1043fn split_qname(n: &str) -> (Option<String>, String) {
1044 if let Some((p, l)) = n.split_once(':') {
1045 (Some(p.to_string()), l.to_string())
1046 } else {
1047 (None, n.to_string())
1048 }
1049}
1050
1051fn eval(expr: &Expr, ctx: &XmlXPathContext<'_>) -> Result<XPathObject, String> {
1054 match expr {
1055 Expr::Or(a, b) => {
1056 let x = xml_xpath_cast_to_boolean(&eval(a, ctx)?);
1057 if x {
1058 Ok(XPathObject::Boolean(true))
1059 } else {
1060 Ok(XPathObject::Boolean(xml_xpath_cast_to_boolean(&eval(b, ctx)?)))
1061 }
1062 }
1063 Expr::And(a, b) => {
1064 let x = xml_xpath_cast_to_boolean(&eval(a, ctx)?);
1065 if !x {
1066 Ok(XPathObject::Boolean(false))
1067 } else {
1068 Ok(XPathObject::Boolean(xml_xpath_cast_to_boolean(&eval(b, ctx)?)))
1069 }
1070 }
1071 Expr::Eq(a, b, eq) => {
1072 let l = eval(a, ctx)?;
1073 let r = eval(b, ctx)?;
1074 Ok(XPathObject::Boolean(compare_eq(&l, &r, ctx) == *eq))
1075 }
1076 Expr::Rel(a, b, ord, or_eq) => {
1077 let ln = xml_xpath_cast_to_number(&eval(a, ctx)?, ctx);
1078 let rn = xml_xpath_cast_to_number(&eval(b, ctx)?, ctx);
1079 let c = ln.partial_cmp(&rn);
1080 let ok = match c {
1081 Some(o) if o == *ord => true,
1082 Some(Ordering::Equal) if *or_eq => true,
1083 _ => false,
1084 };
1085 Ok(XPathObject::Boolean(ok))
1086 }
1087 Expr::Add(a, b, plus) => {
1088 let ln = xml_xpath_cast_to_number(&eval(a, ctx)?, ctx);
1089 let rn = xml_xpath_cast_to_number(&eval(b, ctx)?, ctx);
1090 Ok(XPathObject::Number(if *plus { ln + rn } else { ln - rn }))
1091 }
1092 Expr::Mul(a, b, op) => {
1093 let ln = xml_xpath_cast_to_number(&eval(a, ctx)?, ctx);
1094 let rn = xml_xpath_cast_to_number(&eval(b, ctx)?, ctx);
1095 Ok(XPathObject::Number(match op {
1096 '*' => ln * rn,
1097 'd' => ln / rn,
1098 _ => ln % rn,
1099 }))
1100 }
1101 Expr::Neg(a) => Ok(XPathObject::Number(-xml_xpath_cast_to_number(&eval(a, ctx)?, ctx))),
1102 Expr::Union(a, b) => {
1103 let mut ns = nodeset(eval(a, ctx)?);
1104 ns.extend(nodeset(eval(b, ctx)?));
1105 Ok(XPathObject::NodeSet(sort_unique(ctx.doc, ns)))
1106 }
1107 Expr::Path(p) => eval_path(p, ctx),
1108 Expr::Steps { base, steps } => {
1109 let mut nodes = nodeset(eval(base, ctx)?);
1110 for step in steps {
1111 let mut next = Vec::new();
1112 for n in nodes {
1113 next.extend(axis_nodes(ctx.doc, n, step.axis, &step.test, &ctx.ns));
1114 }
1115 nodes = filter_preds(ctx, sort_unique(ctx.doc, next), &step.preds)?;
1116 }
1117 Ok(XPathObject::NodeSet(nodes))
1118 }
1119 Expr::Filter(e, preds) => {
1120 let o = eval(e, ctx)?;
1121 let ns = nodeset(o);
1122 Ok(XPathObject::NodeSet(filter_preds(ctx, ns, preds)?))
1123 }
1124 Expr::Literal(s) => Ok(XPathObject::String(s.clone())),
1125 Expr::Number(n) => Ok(XPathObject::Number(*n)),
1126 Expr::Var(_) => Err("variables not bound".into()),
1127 Expr::Fun { name, args } => eval_fun(name, args, ctx),
1128 }
1129}
1130
1131fn compare_eq(l: &XPathObject, r: &XPathObject, ctx: &XmlXPathContext<'_>) -> bool {
1132 match (l, r) {
1133 (XPathObject::Boolean(_), _) | (_, XPathObject::Boolean(_)) => {
1134 xml_xpath_cast_to_boolean(l) == xml_xpath_cast_to_boolean(r)
1135 }
1136 (XPathObject::Number(_), _) | (_, XPathObject::Number(_)) => {
1137 xml_xpath_cast_to_number(l, ctx) == xml_xpath_cast_to_number(r, ctx)
1138 }
1139 _ => xml_xpath_cast_to_string(l, ctx) == xml_xpath_cast_to_string(r, ctx),
1140 }
1141}
1142
1143fn nodeset(o: XPathObject) -> Vec<NodeId> {
1144 match o {
1145 XPathObject::NodeSet(v) => v,
1146 _ => vec![],
1147 }
1148}
1149
1150fn sort_unique(doc: &XmlDoc, mut v: Vec<NodeId>) -> Vec<NodeId> {
1151 let order = xml_xpath_order_doc_elems(doc);
1152 v.sort_by_key(|n| order.iter().position(|x| x == n).unwrap_or(usize::MAX));
1153 v.dedup();
1154 v
1155}
1156
1157fn eval_path(path: &PathExpr, ctx: &XmlXPathContext<'_>) -> Result<XPathObject, String> {
1158 let mut nodes = if path.abs {
1159 vec![NodeId::DOCUMENT]
1160 } else {
1161 vec![ctx.node]
1162 };
1163 if path.steps.is_empty() && path.abs {
1164 return Ok(XPathObject::NodeSet(nodes));
1165 }
1166 for step in &path.steps {
1167 let mut next = Vec::new();
1168 for n in nodes {
1169 next.extend(axis_nodes(ctx.doc, n, step.axis, &step.test, &ctx.ns));
1170 }
1171 nodes = filter_preds(ctx, sort_unique(ctx.doc, next), &step.preds)?;
1172 }
1173 Ok(XPathObject::NodeSet(nodes))
1174}
1175
1176fn filter_preds(
1177 ctx: &XmlXPathContext<'_>,
1178 nodes: Vec<NodeId>,
1179 preds: &[Expr],
1180) -> Result<Vec<NodeId>, String> {
1181 let mut cur = nodes;
1182 for pred in preds {
1183 let size = cur.len();
1184 let mut kept = Vec::new();
1185 for (i, n) in cur.iter().enumerate() {
1186 let c2 = XmlXPathContext {
1187 doc: ctx.doc,
1188 node: *n,
1189 position: i + 1,
1190 size,
1191 ns: ctx.ns.clone(),
1192 };
1193 let v = eval(pred, &c2)?;
1194 let pass = match v {
1195 XPathObject::Number(num) => (num as usize) == c2.position,
1196 other => xml_xpath_cast_to_boolean(&other),
1197 };
1198 if pass {
1199 kept.push(*n);
1200 }
1201 }
1202 cur = kept;
1203 }
1204 Ok(cur)
1205}
1206
1207fn axis_nodes(
1208 doc: &XmlDoc,
1209 n: NodeId,
1210 axis: Axis,
1211 test: &NodeTest,
1212 ns: &[(String, String)],
1213) -> Vec<NodeId> {
1214 let mut raw = Vec::new();
1215 match axis {
1216 Axis::SelfAxis => raw.push(n),
1217 Axis::Child => {
1218 let mut c = doc.first_child(n);
1219 while let Some(x) = c {
1220 raw.push(x);
1221 c = doc.next_sibling(x);
1222 }
1223 }
1224 Axis::Attribute => {
1225 let mut a = doc.first_attr(n);
1226 while let Some(x) = a {
1227 raw.push(x);
1228 a = doc.next_sibling(x);
1229 }
1230 }
1231 Axis::Parent => {
1232 if let Some(p) = doc.parent(n) {
1233 raw.push(p);
1234 }
1235 }
1236 Axis::Ancestor => {
1237 let mut p = doc.parent(n);
1238 while let Some(x) = p {
1239 raw.push(x);
1240 p = doc.parent(x);
1241 }
1242 }
1243 Axis::AncestorOrSelf => {
1244 raw.push(n);
1245 let mut p = doc.parent(n);
1246 while let Some(x) = p {
1247 raw.push(x);
1248 p = doc.parent(x);
1249 }
1250 }
1251 Axis::Descendant => collect_desc(doc, n, &mut raw, false),
1252 Axis::DescendantOrSelf => collect_desc(doc, n, &mut raw, true),
1253 Axis::FollowingSibling => {
1254 let mut s = doc.next_sibling(n);
1255 while let Some(x) = s {
1256 raw.push(x);
1257 s = doc.next_sibling(x);
1258 }
1259 }
1260 Axis::PrecedingSibling => {
1261 let mut s = doc.prev_sibling(n);
1262 let mut v = Vec::new();
1263 while let Some(x) = s {
1264 v.push(x);
1265 s = doc.prev_sibling(x);
1266 }
1267 v.reverse();
1268 raw.extend(v);
1269 }
1270 Axis::Following => {
1271 let mut cur = n;
1272 loop {
1273 if let Some(s) = doc.next_sibling(cur) {
1274 collect_desc(doc, s, &mut raw, true);
1275 let mut t = doc.next_sibling(s);
1276 while let Some(x) = t {
1277 collect_desc(doc, x, &mut raw, true);
1278 t = doc.next_sibling(x);
1279 }
1280 cur = s;
1281 if let Some(p) = doc.parent(cur) {
1283 cur = p;
1284 continue;
1285 }
1286 } else if let Some(p) = doc.parent(cur) {
1287 cur = p;
1288 continue;
1289 }
1290 break;
1291 }
1292 }
1293 Axis::Preceding => {
1294 let order = xml_xpath_order_doc_elems(doc);
1296 let mut ancestors = Vec::new();
1297 let mut p = Some(n);
1298 while let Some(x) = p {
1299 ancestors.push(x);
1300 p = doc.parent(x);
1301 }
1302 if let Some(idx) = order.iter().position(|x| *x == n) {
1303 for &id in &order[..idx] {
1304 if !ancestors.contains(&id) {
1305 raw.push(id);
1306 }
1307 }
1308 }
1309 }
1310 Axis::Namespace => {
1311 for (pre, _) in doc.ns_defs(n) {
1312 let dummy = n; let _ = (pre, dummy);
1314 }
1315 }
1316 }
1317 raw.into_iter()
1318 .filter(|id| node_test(doc, *id, test, ns, axis))
1319 .collect()
1320}
1321
1322fn collect_desc(doc: &XmlDoc, n: NodeId, out: &mut Vec<NodeId>, include_self: bool) {
1323 if include_self {
1324 out.push(n);
1325 }
1326 let mut c = doc.first_child(n);
1327 while let Some(x) = c {
1328 collect_desc(doc, x, out, true);
1329 c = doc.next_sibling(x);
1330 }
1331}
1332
1333fn node_test(
1334 doc: &XmlDoc,
1335 id: NodeId,
1336 test: &NodeTest,
1337 ns: &[(String, String)],
1338 axis: Axis,
1339) -> bool {
1340 match test {
1341 NodeTest::Node => true,
1342 NodeTest::Star => {
1343 if axis == Axis::Attribute {
1344 doc.kind(id) == NodeKind::Attribute
1345 } else {
1346 doc.kind(id) == NodeKind::Element
1347 }
1348 }
1349 NodeTest::Text => doc.kind(id) == NodeKind::Text || doc.kind(id) == NodeKind::CData,
1350 NodeTest::Comment => doc.kind(id) == NodeKind::Comment,
1351 NodeTest::Pi(t) => {
1352 doc.kind(id) == NodeKind::Pi && t.as_deref().map(|x| x == doc.name(id)).unwrap_or(true)
1353 }
1354 NodeTest::Name(local, prefix) => {
1355 let kind_ok = if axis == Axis::Attribute {
1356 doc.kind(id) == NodeKind::Attribute
1357 } else {
1358 doc.kind(id) == NodeKind::Element
1359 };
1360 if !kind_ok || doc.name(id) != local {
1361 return false;
1362 }
1363 if let Some(p) = prefix {
1364 let href = ns.iter().find(|(a, _)| a == p).map(|(_, h)| h.as_str());
1365 match href {
1366 Some(h) => doc.ns_uri(id) == Some(h),
1367 None => doc.prefix(id) == Some(p.as_str()),
1368 }
1369 } else {
1370 true
1371 }
1372 }
1373 }
1374}
1375
1376fn eval_fun(name: &str, args: &[Expr], ctx: &XmlXPathContext<'_>) -> Result<XPathObject, String> {
1377 let ev = |i: usize| eval(&args[i], ctx);
1378 let local = name.rsplit(':').next().unwrap_or(name);
1379 match local {
1380 "true" => Ok(XPathObject::Boolean(true)),
1381 "false" => Ok(XPathObject::Boolean(false)),
1382 "not" => Ok(XPathObject::Boolean(!xml_xpath_cast_to_boolean(&ev(0)?))),
1383 "boolean" => Ok(XPathObject::Boolean(xml_xpath_cast_to_boolean(&ev(0)?))),
1384 "number" => {
1385 let o = if args.is_empty() {
1386 XPathObject::NodeSet(vec![ctx.node])
1387 } else {
1388 ev(0)?
1389 };
1390 Ok(XPathObject::Number(xml_xpath_cast_to_number(&o, ctx)))
1391 }
1392 "string" => {
1393 let o = if args.is_empty() {
1394 XPathObject::NodeSet(vec![ctx.node])
1395 } else {
1396 ev(0)?
1397 };
1398 Ok(XPathObject::String(xml_xpath_cast_to_string(&o, ctx)))
1399 }
1400 "last" => Ok(XPathObject::Number(ctx.size as f64)),
1401 "position" => Ok(XPathObject::Number(ctx.position as f64)),
1402 "count" => Ok(XPathObject::Number(nodeset(ev(0)?).len() as f64)),
1403 "local-name" => {
1404 let ns = if args.is_empty() {
1405 vec![ctx.node]
1406 } else {
1407 nodeset(ev(0)?)
1408 };
1409 Ok(XPathObject::String(
1410 ns.first().map(|id| ctx.doc.name(*id).to_string()).unwrap_or_default(),
1411 ))
1412 }
1413 "name" => {
1414 let ns = if args.is_empty() {
1415 vec![ctx.node]
1416 } else {
1417 nodeset(ev(0)?)
1418 };
1419 Ok(XPathObject::String(
1420 ns.first().map(|id| ctx.doc.qname(*id)).unwrap_or_default(),
1421 ))
1422 }
1423 "namespace-uri" => {
1424 let ns = if args.is_empty() {
1425 vec![ctx.node]
1426 } else {
1427 nodeset(ev(0)?)
1428 };
1429 Ok(XPathObject::String(
1430 ns.first()
1431 .and_then(|id| ctx.doc.ns_uri(*id).map(str::to_string))
1432 .unwrap_or_default(),
1433 ))
1434 }
1435 "concat" => {
1436 let mut s = String::new();
1437 for a in args {
1438 s.push_str(&xml_xpath_cast_to_string(&eval(a, ctx)?, ctx));
1439 }
1440 Ok(XPathObject::String(s))
1441 }
1442 "starts-with" => {
1443 let a = xml_xpath_cast_to_string(&ev(0)?, ctx);
1444 let b = xml_xpath_cast_to_string(&ev(1)?, ctx);
1445 Ok(XPathObject::Boolean(a.starts_with(&b)))
1446 }
1447 "contains" => {
1448 let a = xml_xpath_cast_to_string(&ev(0)?, ctx);
1449 let b = xml_xpath_cast_to_string(&ev(1)?, ctx);
1450 Ok(XPathObject::Boolean(a.contains(&b)))
1451 }
1452 "substring-before" => {
1453 let a = xml_xpath_cast_to_string(&ev(0)?, ctx);
1454 let b = xml_xpath_cast_to_string(&ev(1)?, ctx);
1455 Ok(XPathObject::String(
1456 a.split_once(&b).map(|(x, _)| x.to_string()).unwrap_or_default(),
1457 ))
1458 }
1459 "substring-after" => {
1460 let a = xml_xpath_cast_to_string(&ev(0)?, ctx);
1461 let b = xml_xpath_cast_to_string(&ev(1)?, ctx);
1462 Ok(XPathObject::String(
1463 a.split_once(&b).map(|(_, x)| x.to_string()).unwrap_or_default(),
1464 ))
1465 }
1466 "substring" => {
1467 let s = xml_xpath_cast_to_string(&ev(0)?, ctx);
1468 let start = xpath_round(xml_xpath_cast_to_number(&ev(1)?, ctx));
1469 let end = if args.len() > 2 {
1470 start + xpath_round(xml_xpath_cast_to_number(&ev(2)?, ctx))
1471 } else {
1472 f64::INFINITY
1473 };
1474 let out: String = s
1475 .chars()
1476 .enumerate()
1477 .filter(|(i, _)| {
1478 let pos = (*i as f64) + 1.0;
1479 pos >= start && pos < end
1480 })
1481 .map(|(_, c)| c)
1482 .collect();
1483 Ok(XPathObject::String(out))
1484 }
1485 "string-length" => {
1486 let s = if args.is_empty() {
1487 ctx.doc.xml_node_get_content(ctx.node)
1488 } else {
1489 xml_xpath_cast_to_string(&ev(0)?, ctx)
1490 };
1491 Ok(XPathObject::Number(s.chars().count() as f64))
1492 }
1493 "normalize-space" => {
1494 let s = if args.is_empty() {
1495 ctx.doc.xml_node_get_content(ctx.node)
1496 } else {
1497 xml_xpath_cast_to_string(&ev(0)?, ctx)
1498 };
1499 Ok(XPathObject::String(
1500 s.split_whitespace().collect::<Vec<_>>().join(" "),
1501 ))
1502 }
1503 "translate" => {
1504 let s = xml_xpath_cast_to_string(&ev(0)?, ctx);
1505 let from: Vec<char> = xml_xpath_cast_to_string(&ev(1)?, ctx).chars().collect();
1506 let to: Vec<char> = xml_xpath_cast_to_string(&ev(2)?, ctx).chars().collect();
1507 let out: String = s
1508 .chars()
1509 .filter_map(|c| {
1510 if let Some(i) = from.iter().position(|x| *x == c) {
1511 to.get(i).copied()
1512 } else {
1513 Some(c)
1514 }
1515 })
1516 .collect();
1517 Ok(XPathObject::String(out))
1518 }
1519 "floor" => Ok(XPathObject::Number(
1520 xml_xpath_cast_to_number(&ev(0)?, ctx).floor(),
1521 )),
1522 "ceiling" => Ok(XPathObject::Number(
1523 xml_xpath_cast_to_number(&ev(0)?, ctx).ceil(),
1524 )),
1525 "round" => {
1526 let n = xml_xpath_cast_to_number(&ev(0)?, ctx);
1527 Ok(XPathObject::Number(xpath_round(n)))
1528 }
1529 "sum" => {
1530 let ns = nodeset(ev(0)?);
1531 let mut t = 0.0;
1532 for id in ns {
1533 t += xpath_number(&ctx.doc.xml_node_get_content(id));
1534 }
1535 Ok(XPathObject::Number(t))
1536 }
1537 "id" => {
1538 let ids = xml_xpath_cast_to_string(&ev(0)?, ctx);
1539 let mut found = Vec::new();
1540 let order = xml_xpath_order_doc_elems(ctx.doc);
1541 for tok in ids.split_whitespace() {
1542 for id in &order {
1543 if ctx.doc.kind(*id) == NodeKind::Element
1544 && ctx.doc.xml_get_prop(*id, "id").as_deref() == Some(tok)
1545 {
1546 found.push(*id);
1547 }
1548 }
1549 }
1550 Ok(XPathObject::NodeSet(sort_unique(ctx.doc, found)))
1551 }
1552 "lang" => {
1553 let want = xml_xpath_cast_to_string(&ev(0)?, ctx).to_ascii_lowercase();
1554 let mut cur = Some(ctx.node);
1555 let mut lang = None;
1556 while let Some(id) = cur {
1557 if let Some(l) = ctx.doc.xml_get_prop(id, "lang") {
1558 lang = Some(l);
1559 break;
1560 }
1561 cur = ctx.doc.parent(id);
1562 }
1563 let ok = lang
1564 .map(|l| {
1565 let l = l.to_ascii_lowercase();
1566 l == want || l.starts_with(&format!("{want}-"))
1567 })
1568 .unwrap_or(false);
1569 Ok(XPathObject::Boolean(ok))
1570 }
1571 _ => Err(format!("unknown function {name}")),
1572 }
1573}
1574
1575fn xpath_round(n: f64) -> f64 {
1576 if n.is_nan() || n.is_infinite() {
1577 return n;
1578 }
1579 if n == 0.0 {
1580 return n;
1581 }
1582 if n >= 0.0 {
1584 (n + 0.5).floor()
1585 } else {
1586 let f = (n.abs() + 0.5).floor();
1587 if (n.abs() - n.abs().floor()) == 0.5 {
1588 -n.abs().floor()
1589 } else {
1590 -f
1591 }
1592 }
1593}