Skip to main content

XPathContext

Struct XPathContext 

Source
pub struct XPathContext<'doc> {
    pub index: DocIndex<'doc>,
    /* private fields */
}
Expand description

Reusable XPath context for an arena-allocated ArenaDocument.

Build once, evaluate many times. Internally the XPath evaluator is generic over DocIndexLike.

Fields§

§index: DocIndex<'doc>

The flat index used by the evaluator. Exposed so external adapters (e.g. the libxml2 C-ABI shim in sup-xml-compat) can build their own result-object wrappers without rebuilding the index.

Implementations§

Source§

impl<'doc> XPathContext<'doc>

Source

pub fn new(doc: &'doc ArenaDocument) -> Self

Build a strict-mode evaluation context for doc. O(n) in the number of nodes. Equivalent to new_with with default XPathOptions.

Examples found in repository?
examples/profile_xpath_slow_unit.rs (line 66)
59fn main() {
60    let path = std::env::args().nth(1)
61        .expect("usage: profile_xpath_slow_unit <artifact-path>");
62    let bytes = std::fs::read(&path).expect("read artifact");
63    let full = std::str::from_utf8(&bytes).expect("artifact is UTF-8");
64
65    let doc = parse_str(FIXTURE, &ParseOptions::default()).expect("fixture parses");
66    let ctx = XPathContext::new(&doc);
67
68    // Report the predicate-nesting depth of the original artifact so we
69    // can pick a sensible MAX_PREDICATE_NESTING_DEPTH.
70    use sup_xml_core::xpath::ast::max_predicate_nesting;
71    use sup_xml_core::xpath::parse_xpath;
72    match parse_xpath(full) {
73        Ok(e)  => println!("# original artifact parses; predicate-nesting depth = {}", max_predicate_nesting(&e)),
74        Err(e) => println!("# original artifact parse-error: {}",
75                           e.message.chars().take(80).collect::<String>()),
76    }
77
78    println!("# best-of-3 timing of slow-unit reductions");
79    println!("{:>9}  {:>8}  {}", "time", "len", "expression  →  result");
80    println!("{}", "-".repeat(78));
81
82    // ── baseline ──────────────────────────────────────────────────────────
83    time_one(&ctx, "FULL artifact (1018 B)", full);
84
85    // ── strip from the right, halving each time ──────────────────────────
86    time_one(&ctx, "first 512 B",  &full[..full.len().min(512)]);
87    time_one(&ctx, "first 256 B",  &full[..full.len().min(256)]);
88    time_one(&ctx, "first 128 B",  &full[..full.len().min(128)]);
89    time_one(&ctx, "first  64 B",  &full[..full.len().min(64)]);
90
91    // ── isolate specific patterns ────────────────────────────────────────
92    time_one(&ctx, "just nested substring chains",
93        "r*substring(/*substring(/*substring(/*substring(/*substring(/*substring(/*substring(//book[author='Hu'])))))))");
94    time_one(&ctx, "deep //*/*/* descent chain",
95        "//*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*");
96    time_one(&ctx, "nested predicates (the worst case)",
97        "//*[//*[//*[//*[//*[//*[//*[.='x']]]]]]]");
98    time_one(&ctx, "nested //*/* in predicate",
99        "//*[/*//*//*//*//*//*//*//*//*//*]");
100    time_one(&ctx, "wildcard arithmetic chain",
101        "//*/*/*/* * * * * * * * * * //*/*/*");
102    time_one(&ctx, "many top-level *",
103        "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *");
104
105    // ── single-step costs for comparison ─────────────────────────────────
106    time_one(&ctx, "trivial root",                                 "/");
107    time_one(&ctx, "trivial wildcard",                             "/*");
108    time_one(&ctx, "all elements",                                 "//*");
109    time_one(&ctx, "all elements with trivial predicate",          "//*[true()]");
110    time_one(&ctx, "all elements with attribute access",           "//*[@id]");
111    time_one(&ctx, "books filtered by author",                     "//book[author='Hunt']");
112    time_one(&ctx, "substring on string literal",                  "substring('hello', 2, 3)");
113    time_one(&ctx, "substring on node text",                       "substring(//title, 2, 3)");
114}
Source

pub fn new_with(doc: &'doc ArenaDocument, options: XPathOptions) -> Self

Build an evaluation context for doc with explicit options. Use this to opt into libxml2-compatible behaviour for the three spec deviations called out on XPathOptions.

Source

pub fn eval(&self, src: &str) -> Result<XPathValue>

Examples found in repository?
examples/profile_xpath_slow_unit.rs (line 47)
42fn time_one(ctx: &XPathContext, label: &str, expr: &str) {
43    let mut best = std::time::Duration::MAX;
44    let mut last_result = String::new();
45    for _ in 0..3 {
46        let t = Instant::now();
47        let r = ctx.eval(expr);
48        let dt = t.elapsed();
49        if dt < best { best = dt; }
50        last_result = match &r {
51            Ok(_)  => "Ok".to_string(),
52            Err(e) => format!("Err: {}", e.message.chars().take(60).collect::<String>()),
53        };
54    }
55    println!("{:>9.2?}  [{:>4} B]  {label}   →  {last_result}",
56             best, expr.len());
57}
Source

pub fn eval_at(&self, src: &str, context_node: NodeId) -> Result<XPathValue>

Evaluate src against a specific context node. XPath 1.0 allows the context-node to be any node in the document; the libxml2 ABI exposes this via xmlXPathContext.node.

Source

pub fn eval_with( &self, src: &str, context_node: NodeId, bindings: &dyn XPathBindings, ) -> Result<XPathValue>

Evaluate src against context_node, consulting bindings for user-registered functions (extensions= in lxml), variables ($varname), and namespace prefix resolution. This is the entry point the libxml2 C-ABI shim (sup-xml-compat::xpath) uses when its xmlXPathContext carries registered namespaces, functions, or variables.

Source

pub fn id_for_element(&self, ptr: *const Node<'_>) -> Option<NodeId>

Find the index ID for a node identified by its arena pointer. Returns None if the pointer doesn’t match any indexed node. Linear scan; O(n) in document size — call once per evaluation, not per expression step.

Handles attribute pointers too: libxslt sets the XPath context node to an xmlAttr* while iterating @* (<xsl:for-each select="@*">), and the C-ABI layer hands that pointer here cast to *const Node. The match is by raw address, so comparing the indexed &Attribute against ptr is sound regardless of the nominal pointer type.

Source

pub fn eval_bool(&self, src: &str) -> Result<bool>

Source

pub fn eval_str(&self, src: &str) -> Result<String>

Source

pub fn eval_num(&self, src: &str) -> Result<f64>

Source

pub fn eval_strings(&self, src: &str) -> Result<Vec<String>>

Source

pub fn eval_count(&self, src: &str) -> Result<usize>

Source

pub fn eval_node_xml(&self, src: &str) -> Result<Vec<String>>

Evaluate src and return each matched node serialized as XML (the XPath equivalent of xmllint --xpath’s default output).

  • Element / Text / Comment / CData / PI nodes serialize to their subtree XML form, identical to [crate::serialize_node_to_string] with default options.
  • Attribute nodes render as name="value" with the value attribute-escaped.
  • Namespace nodes render as xmlns:prefix="uri" (or xmlns="…" for the default namespace).
  • The synthetic Document node (returned by /) serializes as the whole document via crate::serialize_to_string.

Non-NodeSet results (string / number / boolean) collapse to a single-element vec containing their XPath 1.0 string form, matching Self::eval_strings.

Auto Trait Implementations§

§

impl<'doc> !Freeze for XPathContext<'doc>

§

impl<'doc> !RefUnwindSafe for XPathContext<'doc>

§

impl<'doc> !Send for XPathContext<'doc>

§

impl<'doc> !Sync for XPathContext<'doc>

§

impl<'doc> !UnwindSafe for XPathContext<'doc>

§

impl<'doc> Unpin for XPathContext<'doc>

§

impl<'doc> UnsafeUnpin for XPathContext<'doc>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.