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>
impl<'doc> XPathContext<'doc>
Sourcepub fn new(doc: &'doc ArenaDocument) -> Self
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?
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}Sourcepub fn new_with(doc: &'doc ArenaDocument, options: XPathOptions) -> Self
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.
Sourcepub fn eval(&self, src: &str) -> Result<XPathValue>
pub fn eval(&self, src: &str) -> Result<XPathValue>
Examples found in repository?
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}Sourcepub fn eval_at(&self, src: &str, context_node: NodeId) -> Result<XPathValue>
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.
Sourcepub fn eval_with(
&self,
src: &str,
context_node: NodeId,
bindings: &dyn XPathBindings,
) -> Result<XPathValue>
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.
Sourcepub fn id_for_element(&self, ptr: *const Node<'_>) -> Option<NodeId>
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.
pub fn eval_bool(&self, src: &str) -> Result<bool>
pub fn eval_str(&self, src: &str) -> Result<String>
pub fn eval_num(&self, src: &str) -> Result<f64>
pub fn eval_strings(&self, src: &str) -> Result<Vec<String>>
pub fn eval_count(&self, src: &str) -> Result<usize>
Sourcepub fn eval_node_xml(&self, src: &str) -> Result<Vec<String>>
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"(orxmlns="…"for the default namespace). - The synthetic Document node (returned by
/) serializes as the whole document viacrate::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.