mod ast;
mod axes;
mod budget;
mod compare;
mod context;
mod display;
mod eval;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
mod float;
mod functions;
mod names;
mod parser;
mod typed;
mod value;
pub use ast::{Axis, BinaryOp, Expr, Filter, Func, NodeTest, Step};
pub use context::{EvalContext, EvalError, EvalLimits};
pub use parser::XPathError;
pub use typed::{FromXPath, QueryError, TypeError};
pub use value::Value;
use crate::Limits;
use crate::tree::{Document, ExpandedName, NodeId};
#[derive(Debug, Clone, PartialEq)]
pub struct XPath {
expr: Expr,
namespaces: Vec<(String, String)>,
variables: Vec<ExpandedName>,
}
impl core::fmt::Display for XPath {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
display::Render {
namespaces: &self.namespaces,
}
.expr(&self.expr, f)
}
}
impl XPath {
pub fn compile(expr: &str) -> Result<Self, XPathError> {
Self::compile_with_limits(expr, &Limits::default())
}
pub fn compile_with_namespaces(
expr: &str,
namespaces: &[(&str, &str)],
) -> Result<Self, XPathError> {
Self::compile_with_namespaces_and_limits(
expr,
namespaces,
&Limits::default(),
)
}
pub fn compile_with_limits(
expr: &str,
limits: &Limits,
) -> Result<Self, XPathError> {
Self::compile_with_namespaces_and_limits(expr, &[], limits)
}
pub fn compile_with_namespaces_and_limits(
expr: &str,
namespaces: &[(&str, &str)],
limits: &Limits,
) -> Result<Self, XPathError> {
let compiled = parser::compile(expr, namespaces, limits)?;
Ok(Self {
expr: compiled.expr,
namespaces: namespaces
.iter()
.map(|(p, u)| (p.to_string(), u.to_string()))
.collect(),
variables: compiled.variables,
})
}
#[must_use]
pub fn evaluate(&self, doc: &Document) -> Value {
#[cfg(feature = "tracing")]
let _span = tracing::trace_span!("oxml.xpath.evaluate").entered();
self.lenient(doc, doc.root())
}
#[must_use]
pub fn evaluate_from(&self, doc: &Document, context: NodeId) -> Value {
#[cfg(feature = "tracing")]
let _span = tracing::trace_span!("oxml.xpath.evaluate_from").entered();
self.lenient(doc, context)
}
fn lenient(&self, doc: &Document, context: NodeId) -> Value {
if !self.variables.is_empty() {
return Value::NodeSet(Vec::new());
}
eval::evaluate(doc, &self.expr, context, &EvalContext::default(), false)
.unwrap_or_else(|_| Value::NodeSet(Vec::new()))
}
pub fn evaluate_with(
&self,
doc: &Document,
ctx: &EvalContext,
) -> Result<Value, EvalError> {
self.evaluate_from_with(doc, doc.root(), ctx)
}
pub fn evaluate_from_with(
&self,
doc: &Document,
context: NodeId,
ctx: &EvalContext,
) -> Result<Value, EvalError> {
#[cfg(feature = "tracing")]
let _span = tracing::trace_span!("oxml.xpath.evaluate_with").entered();
if doc.kind(context).is_none() {
return Err(EvalError::Stale);
}
eval::evaluate(doc, &self.expr, context, ctx, true)
}
#[must_use]
pub fn variables(&self) -> &[ExpandedName] {
&self.variables
}
#[must_use]
pub const fn expr(&self) -> &Expr {
&self.expr
}
}