quarb/adapter.rs
1//! The adapter surface: how a data source plugs into the engine.
2//!
3//! The live methods — those the engine currently drives — are
4//! the navigation set ([`root`](AstAdapter::root),
5//! [`children`](AstAdapter::children), [`name`](AstAdapter::name),
6//! [`parent`](AstAdapter::parent)) plus the projection set
7//! ([`traits`](AstAdapter::traits), [`property`](AstAdapter::property),
8//! [`default_value`](AstAdapter::default_value),
9//! [`metadata`](AstAdapter::metadata)). The projection methods have
10//! defaults, so an adapter can implement only what its domain
11//! supports. The reference cluster (`->`, `<-`, `-->`, `<--`,
12//! `=>`), the search fast paths and provenance are documented on
13//! the trait, group by group. See `doc/impl.tex`.
14
15use crate::value::Value;
16
17/// An opaque handle to a node in an arbor.
18///
19/// The engine treats a `NodeId` as an opaque token: it is minted and
20/// interpreted solely by the adapter that produced it. The `u64`
21/// payload is an adapter-private index or key.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct NodeId(pub u64);
24
25/// Per-node data provenance: the three optional components behind
26/// the `:::source` / `:::instant` / `:::dpid` core-metadata keys and
27/// their composite `:::provenance` (`?src@ts#dpid`, kaiv's
28/// spelling). A component is present only where the source genuinely
29/// records it; wrapper adapters layer them ([`or`](Self::or), inner
30/// wins per component).
31#[derive(Debug, Clone, Default, PartialEq)]
32pub struct Provenance {
33 /// Where the datum came from: the document, file, store or
34 /// page — the innermost source that holds the node (a grafted
35 /// node's source is the file the graft opened, not the
36 /// directory above it).
37 pub source: Option<String>,
38 /// The node's path within its source — its name-path from the
39 /// source's root — so a source and a path name the node
40 /// without the engine's id. Filled by the layer that knows
41 /// where the source begins (a mount, a graft), else by the
42 /// engine from the arbor root.
43 pub path: Option<String>,
44 /// When the datum was observed — `(secs, nanos, offset_min)`,
45 /// the shape `Value::Instant` carries — by the rung
46 /// [`instant_from`](Self::instant_from) names.
47 pub instant: Option<(i64, u32, Option<i16>)>,
48 /// Which rung answered the instant: the datum's own
49 /// timestamp, its source's modification time, or the moment of
50 /// reading. `None` with no instant.
51 pub instant_from: Option<InstantFrom>,
52 /// The source-assigned data-point identifier (kaiv `#dpid`).
53 pub dpid: Option<String>,
54}
55
56/// The rung an instant came from — three facts that share one
57/// field: a row's own timestamp, a file's mtime, the clock at the
58/// read.
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum InstantFrom {
61 /// The datum's own timestamp (a kaiv `@ts`, a commit date, a
62 /// row's `modified`).
63 Node,
64 /// The source's modification time (a file's mtime).
65 Source,
66 /// The moment the query read it: the invocation instant.
67 Access,
68}
69
70impl InstantFrom {
71 pub fn name(self) -> &'static str {
72 match self {
73 InstantFrom::Node => "node",
74 InstantFrom::Source => "source",
75 InstantFrom::Access => "access",
76 }
77 }
78}
79
80impl Provenance {
81 /// Component-wise layering: `self` (inner, more specific) wins;
82 /// missing components fill from `outer`. The instant and its
83 /// rung travel together.
84 pub fn or(self, outer: Provenance) -> Provenance {
85 let (instant, instant_from) = if self.instant.is_some() {
86 (self.instant, self.instant_from)
87 } else {
88 (outer.instant, outer.instant_from)
89 };
90 Provenance {
91 source: self.source.or(outer.source),
92 path: self.path.or(outer.path),
93 instant,
94 instant_from,
95 dpid: self.dpid.or(outer.dpid),
96 }
97 }
98
99 /// Nothing recorded — the path does not count, it is derivable.
100 pub fn is_empty(&self) -> bool {
101 self.source.is_none() && self.instant.is_none() && self.dpid.is_none()
102 }
103
104 /// The instant re-labelled as the source's — what a graft does
105 /// to the file's own mtime when it fills a node inside the file.
106 pub fn as_source_rung(mut self) -> Provenance {
107 if self.instant.is_some() {
108 self.instant_from = Some(InstantFrom::Source);
109 }
110 self
111 }
112
113 /// The canonical text of a list: `?a@ts#x;b#y;+N` — one `?`,
114 /// the entries separated by `;` (kaiv's list separator), each
115 /// in its own optionality grammar, the elision marker `+N` last
116 /// when `elided > 0`. `None` when no entry has a component.
117 pub fn canonical_list(entries: &[Provenance], elided: u32) -> Option<String> {
118 // Entries that write alike are one in the text (the path is
119 // not part of it).
120 let mut parts: Vec<String> = Vec::new();
121 for c in entries.iter().filter_map(|e| e.canonical()) {
122 let c = c.trim_start_matches('?').to_string();
123 if !parts.contains(&c) {
124 parts.push(c);
125 }
126 }
127 if parts.is_empty() {
128 return None;
129 }
130 let mut out = format!("?{}", parts.join(";"));
131 if elided > 0 {
132 out.push_str(&format!(";+{elided}"));
133 }
134 Some(out)
135 }
136
137 /// The composite canonical text `?src@ts#dpid` — kaiv's
138 /// optionality grammar (`?src`, `?src@ts`, `?@ts#dpid`, …), the
139 /// instant in Quarb's dashed-extended display form. `None` when
140 /// fully empty.
141 pub fn canonical(&self) -> Option<String> {
142 if self.is_empty() {
143 return None;
144 }
145 let mut out = String::from("?");
146 if let Some(src) = &self.source {
147 out.push_str(src);
148 }
149 if let Some((secs, _, offset)) = self.instant {
150 // kaiv's instant has no sub-second field: whole seconds,
151 // whatever the rung recorded.
152 out.push('@');
153 out.push_str(&crate::temporal::format_instant(secs, 0, offset));
154 }
155 if let Some(dpid) = &self.dpid {
156 out.push('#');
157 out.push_str(dpid);
158 }
159 Some(out)
160 }
161}
162
163/// A node's provenance as a list: several entries where the datum
164/// was recorded from several sources (a kaiv leaf's `?a;b`), and how
165/// many further sources the record elides (its `;+N`). The singular
166/// [`AstAdapter::provenance`] is the list's anchoring first entry.
167#[derive(Debug, Clone, Default, PartialEq)]
168pub struct ProvenanceList {
169 pub entries: Vec<Provenance>,
170 pub elided: u32,
171}
172
173impl ProvenanceList {
174 /// The one-entry list — empty when the entry is.
175 pub fn single(p: Provenance) -> ProvenanceList {
176 ProvenanceList {
177 entries: if p.is_empty() { Vec::new() } else { vec![p] },
178 elided: 0,
179 }
180 }
181
182 /// The anchoring entry (the first), empty when there is none.
183 pub fn first(&self) -> Provenance {
184 self.entries.first().cloned().unwrap_or_default()
185 }
186
187 /// The latest instant any entry carries: a datum with several
188 /// sources is as fresh as its newest.
189 pub fn instant_max(&self) -> Option<(i64, u32, Option<i16>)> {
190 self.entries
191 .iter()
192 .filter_map(|p| p.instant)
193 .max_by(|a, b| (a.0, a.1).cmp(&(b.0, b.1)))
194 }
195
196 /// The canonical list text, `?a@ts#x;b#y;+N`.
197 pub fn canonical(&self) -> Option<String> {
198 Provenance::canonical_list(&self.entries, self.elided)
199 }
200
201 /// Component-wise layering of every entry over `outer` (see
202 /// [`Provenance::or`]); an empty list takes `outer` as its one
203 /// entry.
204 pub fn or(self, outer: Provenance) -> ProvenanceList {
205 if self.entries.is_empty() {
206 return ProvenanceList {
207 entries: ProvenanceList::single(outer).entries,
208 elided: self.elided,
209 };
210 }
211 ProvenanceList {
212 entries: self
213 .entries
214 .into_iter()
215 .map(|p| p.or(outer.clone()))
216 .collect(),
217 elided: self.elided,
218 }
219 }
220}
221
222/// The direction of a crosslink walk, for the batching hint
223/// [`AstAdapter::prefetch_links`].
224#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
225pub enum LinkDir {
226 /// `->`: the links a node carries.
227 Out,
228 /// `<-`: the links that reach a node.
229 In,
230}
231
232/// A reference to a document outside the arbor, as an adapter read
233/// it from its own reference syntax: which document, and where in
234/// it. The engine treats both as opaque.
235#[derive(Debug, Clone, PartialEq, Eq)]
236pub struct ExternalRef {
237 /// The document's identity (a URL, a path, a key — whatever
238 /// the mounts are registered under).
239 pub document: String,
240 /// The location within that document, for the adapter that
241 /// owns it to resolve; `None` for the document itself.
242 pub within: Option<String>,
243}
244
245impl ExternalRef {
246 /// Read a URI reference: everything before the first `#` is the
247 /// document, a non-empty fragment after it the location. A
248 /// convenience for adapters whose references are URIs (html,
249 /// the text level, a JSON `$ref`); an adapter with another
250 /// syntax builds the two parts its own way.
251 pub fn uri(reference: &str) -> Self {
252 match reference.split_once('#') {
253 Some((document, fragment)) => ExternalRef {
254 document: document.to_string(),
255 within: (!fragment.is_empty()).then(|| fragment.to_string()),
256 },
257 None => ExternalRef {
258 document: reference.to_string(),
259 within: None,
260 },
261 }
262 }
263
264 /// The reference as one URI again.
265 pub fn to_uri(&self) -> String {
266 match &self.within {
267 Some(w) => format!("{}#{w}", self.document),
268 None => self.document.clone(),
269 }
270 }
271}
272
273/// What one navigation step asks of an adapter, lowered for
274/// [`AstAdapter::select`]: the axis, the name matcher, the
275/// predicate conjuncts the engine can express as plain comparisons,
276/// and — when the stage after the step consumes them — a limit and
277/// an order. An adapter answers from an index what it can and names
278/// what it applied; the engine tests the rest.
279#[derive(Debug, Clone)]
280pub struct StepPlan<'a> {
281 /// The axis: a child hop or a descendant search.
282 pub axis: PlanAxis,
283 /// The name the step matches, when it is one literal name.
284 pub name: Option<&'a str>,
285 /// The leading predicates lowered to comparisons, in order.
286 pub conjuncts: Vec<Conjunct<'a>>,
287 /// How many results the stage after the step keeps, when it
288 /// keeps a prefix (`[..n]`, `@| first(n)`).
289 pub limit: Option<usize>,
290 /// The property the stage after the step orders by, and whether
291 /// ascending.
292 pub order: Option<(&'a str, bool)>,
293}
294
295/// The axis of a [`StepPlan`].
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
297pub enum PlanAxis {
298 /// `/name`: the children.
299 Child,
300 /// `//name`: the descendants, each with its depth.
301 Descendant,
302}
303
304/// One lowered predicate of a [`StepPlan`].
305#[derive(Debug, Clone)]
306pub enum Conjunct<'a> {
307 /// `[::prop = value]` under the engine's standard equality.
308 Equals(&'a str, &'a Value),
309}
310
311/// What an adapter answers to a [`StepPlan`]: the nodes (each with
312/// its depth from the step's base; children are depth 1) in
313/// document order, and which conjuncts the adapter already applied
314/// (the engine runs the rest).
315#[derive(Debug, Clone, Default)]
316pub struct Selection {
317 pub nodes: Vec<(NodeId, usize)>,
318 pub applied: Vec<bool>,
319}
320
321/// What reaching one step's nodes would cost, as an adapter can
322/// say before running it ([`AstAdapter::estimate`]): intervals, a
323/// request count, a latency class and how sure the adapter is. A
324/// host composes these along a query ([`crate::plan::Plan`]) and
325/// refuses before running when a budget is exceeded.
326#[derive(Debug, Clone, PartialEq)]
327pub struct Estimate {
328 /// The nodes the step yields, from the least to the most.
329 pub rows: (u64, u64),
330 /// The bytes read to yield them, when the adapter can say (a
331 /// warehouse's dry run, a file's size).
332 pub bytes: Option<(u64, u64)>,
333 /// Round trips to a remote, when any.
334 pub requests: Option<u32>,
335 pub latency: Latency,
336 pub confidence: Confidence,
337}
338
339/// Where a step's cost is paid.
340#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
341pub enum Latency {
342 /// The nodes are in memory.
343 Memory,
344 /// A local file or store.
345 Local,
346 /// A network round trip.
347 Remote,
348}
349
350/// How much an [`Estimate`] is worth.
351#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
352pub enum Confidence {
353 /// A guess from statistics or defaults.
354 Guess,
355 /// An upper bound (a clustered table's dry run, a partition's
356 /// size): the actual may come in lower.
357 Bound,
358 /// Exact: the catalog or the store knows.
359 Exact,
360}
361
362impl Estimate {
363 /// `self` followed by `next` along one branch: the rows are
364 /// the next step's, the bytes and requests add, the latency is
365 /// the slower, the confidence the weaker.
366 pub fn then(&self, next: &Estimate) -> Estimate {
367 Estimate {
368 rows: next.rows,
369 bytes: match (self.bytes, next.bytes) {
370 (Some(a), Some(b)) => Some((a.0 + b.0, a.1 + b.1)),
371 (a, None) => a,
372 (None, b) => b,
373 },
374 requests: match (self.requests, next.requests) {
375 (Some(a), Some(b)) => Some(a + b),
376 (a, None) => a,
377 (None, b) => b,
378 },
379 latency: self.latency.max(next.latency),
380 confidence: self.confidence.min(next.confidence),
381 }
382 }
383
384 /// `self` beside `other` (two branches of one query): rows,
385 /// bytes and requests add.
386 pub fn beside(&self, other: &Estimate) -> Estimate {
387 Estimate {
388 rows: (self.rows.0 + other.rows.0, self.rows.1 + other.rows.1),
389 ..self.then(other)
390 }
391 }
392}
393
394/// [`AstAdapter::select`]'s default, through the individual hooks:
395/// a child hop by `children_named`; a descendant search by
396/// `descendants_where` when the plan leads with an equality the
397/// adapter indexes, else `descendants_named`. A wrapper that
398/// translates node ids forwards `select` to its inner adapter and
399/// falls back to this over its own hooks.
400pub fn select_by_hooks<A: AstAdapter + ?Sized>(
401 adapter: &A,
402 from: NodeId,
403 plan: &StepPlan<'_>,
404) -> Option<Selection> {
405 let name = plan.name?;
406 match plan.axis {
407 PlanAxis::Child => Some(Selection {
408 nodes: adapter
409 .children_named(from, name)
410 .into_iter()
411 .map(|n| (n, 1))
412 .collect(),
413 applied: vec![false; plan.conjuncts.len()],
414 }),
415 PlanAxis::Descendant => {
416 if let Some(Conjunct::Equals(prop, value)) = plan.conjuncts.first()
417 && let Some(nodes) = adapter.descendants_where(from, name, prop, value)
418 {
419 let mut applied = vec![false; plan.conjuncts.len()];
420 applied[0] = true;
421 return Some(Selection { nodes, applied });
422 }
423 let nodes = adapter.descendants_named(from, name)?;
424 Some(Selection {
425 nodes,
426 applied: vec![false; plan.conjuncts.len()],
427 })
428 }
429 }
430}
431
432/// The probe behind [`AstAdapter::has_node`]'s default: run a
433/// closure that may panic on a foreign node id, answering `false`
434/// when it does, without the panic reaching stderr. The process's
435/// hook is wrapped once (the first probe installs the wrapper,
436/// which defers to whatever hook was there) and a thread-local
437/// flag silences it only on the probing thread.
438mod probe {
439 use std::cell::Cell;
440 use std::sync::Once;
441
442 thread_local! {
443 static SILENT: Cell<bool> = const { Cell::new(false) };
444 }
445 static INSTALL: Once = Once::new();
446
447 pub(super) fn silently<R: Default>(f: impl FnOnce() -> R) -> R {
448 INSTALL.call_once(|| {
449 let previous = std::panic::take_hook();
450 std::panic::set_hook(Box::new(move |info| {
451 if !SILENT.with(|s| s.get()) {
452 previous(info);
453 }
454 }));
455 });
456 SILENT.with(|s| s.set(true));
457 let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).unwrap_or_default();
458 SILENT.with(|s| s.set(false));
459 out
460 }
461}
462
463/// The interface a data source implements to be queried by Quarb.
464///
465/// An adapter maps its native structure onto the arbor model: a tree
466/// backbone whose edges carry *names*. The engine drives navigation
467/// purely through this trait, so the same query language runs over
468/// any adapter. Three methods are required (`root`, `children`,
469/// `name`); every other one has a default an adapter overrides
470/// only where its substrate has more to say. The methods fall into
471/// groups:
472///
473/// - **The tree**: `root`, `children`, `name`, `parent`,
474/// `child_host` (a transparent link lends its target's
475/// children), `non_tree_nodes` (joined by crosslinks alone),
476/// `has_node`.
477/// - **Naming and traits**: `answers_to` (per-node spelling
478/// aliases, consulted wherever a literal name is matched),
479/// `traits`, `has_trait`.
480/// - **Properties and metadata**: `property`, `properties`,
481/// `property_names`, `default_value`, `metadata`,
482/// `aliased_metadata`.
483/// - **References**, three protocols the engine consults in this
484/// order. *Materialized edges*: `links` / `backlinks` (the `->` /
485/// `<-` hops) with `link_property` and the `prefetch_links`
486/// batching hint. *Property resolution* (`::prop-->`, `-->`,
487/// `<--`): `resolve`, `ref_property`, `ref_label`,
488/// `resolve_needs_label`, `reverse_resolve`, `reverse_label`,
489/// `can_refer_to`, `reference_properties`,
490/// `walks_for_references`, `resolves_references`. *Cross-document*
491/// (a reference naming another document): `external_ref`,
492/// `resolve_fragment`, `document_by_ref`, `tree_identities`; the
493/// engine joins the identity against the tree the reference was
494/// written in and lands through the host's registered targets,
495/// then `document_by_ref`, then the identities, and records what
496/// it could not reach. The engine performs no I/O for any of it.
497/// - **Declared indexes** (a model's `index SCOPE::prop;`):
498/// `index_lookup`, `index_referrers`.
499/// - **Search fast paths**: [`select`](Self::select) — one plan
500/// per step (axis, name matcher, the conjuncts the engine can
501/// lower, a limit, an order) an adapter may answer from an index;
502/// its default folds `children_named`, `descendants_named` and
503/// `descendants_where`, which an adapter may still implement
504/// individually. A fast path changes cost, never the answer.
505/// - **Evaluation hints**: `quantifier_bound` (the depth the
506/// adapter's own structures need; a run may override it),
507/// `unit_scale`.
508/// - **Provenance**: `provenance`, `provenance_list`.
509///
510/// Pairs the engine relies on being observationally equal, so an
511/// override must keep them so: `children_named(n, x)` is
512/// `children(n)` filtered by `answers_to(_, x)` (except deliberate
513/// container-scoped aliasing, documented on the method);
514/// `has_trait(n, t)` is `t ∈ traits(n)`; `property_names(n)` covers
515/// the names of `properties(n)`, the `ref_property`, and the child
516/// names; `provenance_list(n)`'s first entry is `provenance(n)`;
517/// `select` answers exactly what the walk would find. The
518/// conformance suite ([`crate::conformance::check`]) tests these
519/// over a sample of an adapter's nodes.
520///
521/// Optional capabilities with a single implementor each — fine to
522/// leave at the default: `unit_scale` (kaiv's units),
523/// `descendants_where` (the corpus reading's positional index),
524/// `child_host` (filesystem symlinks), `aliased_metadata` (git's
525/// closed surface), `provenance_list` (kaiv's layered sources),
526/// `index_lookup` / `index_referrers` (a model's declared indexes).
527pub trait AstAdapter {
528 /// The root node — the initial navigation context.
529 fn root(&self) -> NodeId;
530
531 /// The tree children of `node`, in document order.
532 ///
533 /// Returns an empty vector for a leaf (or an unreadable node).
534 fn children(&self, node: NodeId) -> Vec<NodeId>;
535
536 /// The name of `node` — the label of its incoming tree edge.
537 ///
538 /// `None` when the adapter leaves a node unnamed (typically the
539 /// root; e.g. the filesystem root `/` carries no name).
540 fn name(&self, node: NodeId) -> Option<String>;
541
542 /// The parent of `node`, or `None` for the root.
543 fn parent(&self, _node: NodeId) -> Option<NodeId> {
544 None
545 }
546
547 /// The traits of `node` — its adapter-defined classifications,
548 /// used by `<trait>` navigation filters (e.g. a filesystem
549 /// adapter's `<dir>`, `<code>`, `<image>`).
550 fn traits(&self, _node: NodeId) -> Vec<String> {
551 Vec::new()
552 }
553
554 /// A named property of `node` — `::prop`. `None` if absent.
555 fn property(&self, _node: NodeId, _name: &str) -> Option<Value> {
556 None
557 }
558
559 /// The node's own properties, enumerated — name and value, in
560 /// the source's order — for readings that keep values off the
561 /// tree (the JSON property reading, where an object's scalar
562 /// fields are no nodes). The serializers (`| json`, `| xml`,
563 /// `| kaiv`) merge them with the children, a child winning a
564 /// shared name. The default is none: for a reading whose values
565 /// are all nodes, the children already say everything.
566 fn properties(&self, _node: NodeId) -> Vec<(String, Value)> {
567 Vec::new()
568 }
569
570 /// Whether a reverse resolution or a value match with no scope
571 /// may walk the whole arbor to find its referrers. An adapter
572 /// over a small, in-memory source opens to the walk; a store or
573 /// a service does not, and such a query is refused with a message
574 /// asking for a scope — an empty answer could not be told from an
575 /// absence of referrers. A declared reference (a foreign key, a
576 /// model `ref`) answers through [`reverse_resolve`](Self::reverse_resolve)
577 /// and needs no walk.
578 fn walks_for_references(&self) -> bool {
579 false
580 }
581
582 /// Whether a reference *declared* on `source` — a catalog key, a
583 /// model `ref`, a reference property the format defines — can
584 /// land in `target`'s container. A question of the schema, never
585 /// of the data; `None` where nothing is declared at the node.
586 /// The default: a node with a reference property of its own can
587 /// refer anywhere (an href may point at any element).
588 fn can_refer_to(&self, source: NodeId, _target: NodeId) -> Option<bool> {
589 self.ref_property(source).map(|_| true)
590 }
591
592 /// The reference properties declared on `source` that can land
593 /// in `target`'s container: a catalog's key columns into the
594 /// target's table, the fields of a model's `ref`s, the node's
595 /// own reference property. What `t<--` and `t<--(SCOPE)` read
596 /// when the query names no property; empty where none is
597 /// declared.
598 fn reference_properties(&self, source: NodeId, _target: NodeId) -> Vec<String> {
599 self.ref_property(source).into_iter().collect()
600 }
601
602 /// Whether resolving `property` of `node` needs a reference
603 /// label, because the property itself declares no target: a
604 /// column with no key, or any property of a property graph,
605 /// where a value names a node only under a node label. A
606 /// label-less `::p-->` over such a property is refused with the
607 /// remedy, since an empty answer would read as "points nowhere".
608 fn resolve_needs_label(&self, _node: NodeId, _property: &str) -> bool {
609 false
610 }
611
612 /// The reference label under which a referrer's property is
613 /// also resolved when a reverse arrow asks about `target` and
614 /// the query writes no label. `None` (the default): only the
615 /// label-less resolution counts. An adapter whose label names a
616 /// container (a table, a collection, a node label) returns the
617 /// one `target` stands in, so `/authors/1<--(/books/*::author_id)`
618 /// reads `author_id` as a key into `authors` without the query
619 /// saying so. The engine derives no label of its own.
620 fn reverse_label(&self, _target: NodeId) -> Option<String> {
621 None
622 }
623
624 /// The trees of this arbor other trees refer to by name: each
625 /// identity (a URL, a path, any string a reference may hold)
626 /// with the node that stands for the tree. An identity ending
627 /// in a separator is a base: every identity beneath it names
628 /// the node reached beneath the base's node by the remainder's
629 /// segments. Declared in a model (`tree IDENTITY: PATH;`) and
630 /// automatic for a mount's own target; the engine reads nothing
631 /// inside an identity beyond prefix and separator.
632 fn tree_identities(&self) -> Vec<(String, NodeId)> {
633 Vec::new()
634 }
635
636 /// The names of the properties a node has — what "any
637 /// property" means for `<--(SCOPE::*)`. By default its off-tree
638 /// properties, its reference property, and its children's names
639 /// (a row's columns, an object's keys are properties under the
640 /// same names); an adapter whose properties are not children
641 /// (an element's attributes) lists them.
642 fn property_names(&self, node: NodeId) -> Vec<String> {
643 let mut ps: Vec<String> = self.properties(node).into_iter().map(|(k, _)| k).collect();
644 if let Some(r) = self.ref_property(node)
645 && !ps.contains(&r)
646 {
647 ps.push(r);
648 }
649 for c in self.children(node) {
650 if let Some(name) = self.name(c)
651 && !ps.contains(&name)
652 {
653 ps.push(name);
654 }
655 }
656 ps
657 }
658
659 /// Whether this source resolves references at all. An adapter
660 /// with no notion of a reference (a CSV file, a directory, a
661 /// log stream) says no, and a reverse arrow over it is refused
662 /// with the value match as the remedy, rather than answered
663 /// with an empty set that would read as "nothing points here".
664 fn resolves_references(&self) -> bool {
665 true
666 }
667
668 /// `index SCOPE::prop;` — the nodes of the declared scope whose
669 /// property holds `value`, from an index the model keeps; `None`
670 /// when no declaration covers the pair (the walk answers). An
671 /// index changes cost, never the answer: the caller still tests
672 /// each node it names.
673 fn index_lookup(&self, _scope: &str, _property: &str, _value: &Value) -> Option<Vec<NodeId>> {
674 None
675 }
676 /// The declared-scope nodes whose `property` resolves (under the
677 /// relation `label`, when given) to `target`, from the same index;
678 /// `None` when no declaration covers the pair.
679 fn index_referrers(
680 &self,
681 _scope: &str,
682 _property: &str,
683 _label: Option<&str>,
684 _target: NodeId,
685 ) -> Option<Vec<NodeId>> {
686 None
687 }
688
689 /// The nodes of the arbor that are not in the tree — joined to
690 /// it by crosslinks alone, with no parent and no path. The
691 /// universal search `*//` reaches them after the tree, in this
692 /// order; `:::is-non-tree` is true on them. The default is
693 /// none: every shipped adapter puts every node in the tree.
694 fn non_tree_nodes(&self) -> Vec<NodeId> {
695 Vec::new()
696 }
697
698 /// The node whose children an explicit child step from `node`
699 /// reads. The default is `node` itself. An adapter whose tree
700 /// holds transparent links (a filesystem's symlink to a
701 /// directory) answers the link's target, so `/link/name` lands
702 /// on the target's own child — the same node `/target/name`
703 /// reaches. The descendant walk never consults this: it reads
704 /// [`children`](Self::children) alone, so a linked subtree is
705 /// walked once, under its own parent, and a cycle of links
706 /// cannot trap it.
707 fn child_host(&self, node: NodeId) -> NodeId {
708 node
709 }
710
711 /// The children of `node` whose edge name is exactly `name` —
712 /// the engine's fast path for name-matcher child hops. The
713 /// default filters [`children`](Self::children); an adapter
714 /// whose containers cannot be enumerated (permission-scoped or
715 /// unbounded remote trees) overrides this with a direct,
716 /// name-addressed lookup. Must be observationally identical to
717 /// the default wherever enumeration works. The adapter owns the
718 /// name test: it may deliberately *alias* — resolve a name to a
719 /// node whose edge name differs (git revision syntax landing on
720 /// a hash-named commit) — and the engine will not re-filter.
721 /// Container-scoped resolution like that stays child-axis only;
722 /// per-node spelling aliases belong in
723 /// [`answers_to`](Self::answers_to), which this default
724 /// consults.
725 fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
726 self.children(node)
727 .into_iter()
728 .filter(|&c| self.answers_to(c, name))
729 .collect()
730 }
731
732 /// Whether `node` answers to `name` as a literal spelling
733 ///. The default is canonical equality. An adapter
734 /// may declare per-node aliases — a social feed spelling a
735 /// handle `@alice` while the stripped `alice` is the hop name —
736 /// and the engine consults this wherever a *literal* name is
737 /// matched, on every axis, so one override keeps `/@alice` and
738 /// `//@alice` in agreement. `:::name` stays canonical (locators
739 /// and reflection print it; an alias is a way in, never a way
740 /// out), and name patterns (`~(...)`, `*`) test the canonical
741 /// name only.
742 fn answers_to(&self, node: NodeId, name: &str) -> bool {
743 self.name(node).as_deref() == Some(name)
744 }
745
746 /// Whether `node` bears `name` as a trait, as written in a
747 /// `<...>` clause. The default is membership in
748 /// [`traits`](Self::traits); an adapter may declare aliases the
749 /// way [`answers_to`](Self::answers_to) does for names — a
750 /// model file's `alias <chunk> <block>;` or
751 /// `alias <s/^tag://i>;` — and the engine consults this
752 /// wherever a written trait is matched. Reflection and
753 /// serialization print the canonical traits only.
754 fn has_trait(&self, node: NodeId, name: &str) -> bool {
755 self.traits(node).iter().any(|t| t == name)
756 }
757
758 /// The default projection of `node` — bare `::`, adapter-specific
759 /// (a filesystem adapter returns file content).
760 fn default_value(&self, _node: NodeId) -> Option<Value> {
761 None
762 }
763
764 /// Adapter-defined metadata — `::::key` (a filesystem adapter's
765 /// `size`, `modified`, `permissions`, …). `None` if absent.
766 fn metadata(&self, _node: NodeId, _key: &str) -> Option<Value> {
767 None
768 }
769
770 /// Metadata keys this adapter also answers at `::` (ruling
771 /// #29), for the given node — per node, because a composite
772 /// adapter (a multi-mount, a graft) answers for whichever
773 /// document owns it. Only an adapter whose property surface is *closed* —
774 /// fixed by the adapter, never grown by document content — may
775 /// declare aliases: a git commit cannot sprout a `short` field,
776 /// where a JSON object can sprout anything. Data always wins:
777 /// the engine consults `property` first and falls through only
778 /// when it answers `None` for that node, so an alias can never
779 /// shadow a document. Core metadata (`:::`) is never aliased —
780 /// `name`, `id` and `index` exist on every node, and aliasing
781 /// them would change what `::name` means wherever a document
782 /// happens to lack that field.
783 fn aliased_metadata(&self, _node: NodeId) -> &'static [&'static str] {
784 &[]
785 }
786
787 /// Outgoing crosslinks from `node`, as `(label, target)` pairs,
788 /// for `->` navigation (a filesystem adapter's symlinks).
789 fn links(&self, _node: NodeId) -> Vec<(String, NodeId)> {
790 Vec::new()
791 }
792
793 /// Incoming crosslinks to `node`, as `(label, source)` pairs, for
794 /// `<-` navigation. May be expensive (an adapter that does not
795 /// precompute edges must search for referrers).
796 fn backlinks(&self, _node: NodeId) -> Vec<(String, NodeId)> {
797 Vec::new()
798 }
799
800 /// Resolve a cross-reference: `::property-->hint` maps `node`'s
801 /// `property` (a value that references another node) to its target,
802 /// with an optional adapter-specific `hint` (a relation type such as
803 /// an html `rel`, or a target container such as a table). A JSON
804 /// adapter resolves a `$ref` JSON Pointer; `None` if unresolvable.
805 fn resolve(&self, _node: NodeId, _property: &str, _hint: Option<&str>) -> Option<NodeId> {
806 None
807 }
808
809 /// The external reference `node`'s `property` holds, if any: the
810 /// identity of a document *outside this arbor* and, where the
811 /// reference says so, the location within it — both as the
812 /// adapter reads them out of its own reference syntax (for html,
813 /// the anchor's absolute URL and its fragment). The engine does
814 /// not look inside either string: it asks for the document among
815 /// the mounts (the host's registration, then
816 /// [`document_by_ref`](Self::document_by_ref)), and hands the
817 /// location to [`resolve_fragment`](Self::resolve_fragment) on
818 /// the document it found. Consulted by `::property-->` when
819 /// in-document resolution misses; an unmounted document lands
820 /// among the run's unresolved external references for the
821 /// host's acquisition loop. The engine itself never fetches.
822 fn external_ref(
823 &self,
824 _node: NodeId,
825 _property: &str,
826 _hint: Option<&str>,
827 ) -> Option<ExternalRef> {
828 None
829 }
830
831 /// The element `fragment` names inside the document `node`
832 /// belongs to — html's `id` lookup. The landing rung of a
833 /// fragment-carrying external reference, once its document is
834 /// mounted.
835 fn resolve_fragment(&self, _node: NodeId, _fragment: &str) -> Option<NodeId> {
836 None
837 }
838
839 /// The property carrying `node`'s own reference, for the bare
840 /// arrow (`//a-->`): an html anchor's `href`, an iframe's
841 /// `src`. `None` for a node that references nothing.
842 fn ref_property(&self, _node: NodeId) -> Option<String> {
843 None
844 }
845
846 /// The relation name a resolution edge from `node` via
847 /// `property` carries — html's `rel` attribute. `None` falls
848 /// back to the property name as the edge label.
849 fn ref_label(&self, _node: NodeId, _property: &str) -> Option<String> {
850 None
851 }
852
853 /// A property of the crosslink `source --label--> target` — the
854 /// `$-::prop` read. Adapters whose edges carry data (a property
855 /// graph's relationship properties) override this; `None` if the
856 /// edge is bare or unknown. Where parallel edges share source,
857 /// label, and target, the adapter answers for one of them,
858 /// consistently.
859 fn link_property(
860 &self,
861 _source: NodeId,
862 _label: &str,
863 _target: NodeId,
864 _name: &str,
865 ) -> Option<Value> {
866 None
867 }
868
869 /// The document this arbor holds under an external-reference
870 /// identifier (a URL, fragment-stripped), when it can answer
871 /// without the host's registration: the landing rung of `-->`
872 /// after the mounted-document table misses. An adapter over a
873 /// store of pages answers by URL lookup; the engine performs
874 /// no IO either way.
875 fn document_by_ref(&self, _id: &str) -> Option<NodeId> {
876 None
877 }
878
879 /// Every node whose `property` (or, when `None`, own reference
880 /// property) resolves to `node`, in document order — the
881 /// reverse index behind `<--`. `None` keeps the engine's walk
882 /// of the whole arbor; `Some` is the adapter's complete answer.
883 fn reverse_resolve(
884 &self,
885 _node: NodeId,
886 _property: Option<&str>,
887 _hint: Option<&str>,
888 ) -> Option<Vec<NodeId>> {
889 None
890 }
891
892 /// The descendants of `node` that answer to `name`, each with
893 /// its depth (children are depth 1), in document order — the
894 /// fast path behind `//name` for an adapter whose substrate
895 /// indexes names. `None` keeps the engine's walk; `Some` must
896 /// be exactly what the walk would find.
897 fn descendants_named(&self, _node: NodeId, _name: &str) -> Option<Vec<(NodeId, usize)>> {
898 None
899 }
900
901 /// The descendants of `node` named `name` whose property
902 /// `property` equals `value`, each with its depth, in document
903 /// order — the fast path behind `//name[::property = "value"]`
904 /// for an adapter that indexes a property (the corpus reading's
905 /// positional index answers `//token[::lower = "tom"]` from it).
906 /// `None` keeps the engine's walk and its comparison; `Some`
907 /// must be exactly what the walk would find under the standard
908 /// equality, so an adapter answers only the values it can
909 /// compare as the engine does (text against text).
910 fn descendants_where(
911 &self,
912 _node: NodeId,
913 _name: &str,
914 _property: &str,
915 _value: &Value,
916 ) -> Option<Vec<(NodeId, usize)>> {
917 None
918 }
919
920 /// The search fast path: the nodes one step reaches from `from`
921 /// under `plan`, answered from an index — `None` keeps the
922 /// engine's walk (the default folds `children_named`,
923 /// `descendants_named` and `descendants_where`:
924 /// [`select_by_hooks`]). `Some` must be exactly what the walk
925 /// would find for the conjuncts it marks applied, under the
926 /// engine's standard equality; the engine tests the remaining
927 /// conjuncts and every other predicate on the nodes returned. A
928 /// child plan's answer is never re-filtered by name (the adapter
929 /// owns the name test, as `children_named` does); a descendant
930 /// plan's nodes still pass the nameless tests.
931 fn select(&self, from: NodeId, plan: &StepPlan<'_>) -> Option<Selection> {
932 select_by_hooks(self, from, plan)
933 }
934 /// The dry-run form of [`select`](Self::select): which of the
935 /// plan's conjuncts the adapter would apply, decided from its
936 /// catalog alone (a column's existence and type, an index's
937 /// presence), with no node in hand. `scope` is the literal path
938 /// from the root to the step's base (`["invoices"]` for
939 /// `/invoices/*[...]`), empty at the root. `None` when the
940 /// adapter cannot say — the static plan then counts every
941 /// conjunct as the engine's.
942 fn select_plan(&self, _scope: &[&str], _plan: &StepPlan<'_>) -> Option<Vec<bool>> {
943 None
944 }
945 /// What reaching one step's nodes would cost, said from the
946 /// catalog with no node in hand (`scope` as for
947 /// [`select_plan`](Self::select_plan)): a warehouse's dry run,
948 /// a table's statistics, a file's size, an arena's count.
949 /// `None` where the adapter has no estimator — the plan then
950 /// reads "unknown" for the step.
951 fn estimate(&self, _scope: &[&str], _plan: &StepPlan<'_>) -> Option<Estimate> {
952 None
953 }
954 /// How many nodes one step reaches from `from` under `plan`,
955 /// without materializing them — `@| count` right after the step
956 /// asks before walking. `None` walks; `Some` must be exactly the
957 /// walk's count for the conjuncts the plan carries (every one of
958 /// them, or none: a count cannot be partially applied). SQL:
959 /// `SELECT COUNT(*)`; a document index: the bucket's length.
960 fn count(&self, _from: NodeId, _plan: &StepPlan<'_>) -> Option<usize> {
961 None
962 }
963 /// A batching hint, the projection twin of `prefetch_links`: the
964 /// engine is about to read the properties `names` of every node
965 /// in `nodes` (the next stage projects them). A remote adapter
966 /// fetches the columns for the frontier in one round trip;
967 /// semantics are unchanged either way.
968 fn prefetch_properties(&self, _nodes: &[NodeId], _names: &[&str]) {}
969 /// A batching hint: the engine is about to follow `dir`
970 /// crosslinks from every node in `nodes`. An adapter backed by
971 /// a store may fetch them in one statement; semantics are
972 /// unchanged either way.
973 fn prefetch_links(&self, _nodes: &[NodeId], _dir: LinkDir) {}
974
975 /// The quantifier bound N_max: the depth to which open-ended path
976 /// quantifiers (`+`, `*`, `{m,}`) expand, and the ceiling of any
977 /// explicit `{m,n}` (the effective upper bound is min(n, N_max)).
978 /// An adapter whose natural structures run deep may raise it; a
979 /// run may override it (`Run::quantifier_bound`, the CLI's
980 /// `--quantifier-bound`). The shell gate and the invocation
981 /// instant are the run's alone (`Run::allow_shell`, `Run::now`).
982 fn quantifier_bound(&self) -> usize {
983 32
984 }
985
986 /// The data provenance of `node` — the `:::source` /
987 /// `:::instant` / `:::dpid` / `:::provenance` core-metadata
988 /// keys. Empty by default: an adapter answers only the
989 /// components its substrate genuinely records (never the
990 /// invocation clock); wrapper adapters fill missing components
991 /// from what they know — the mount its target, a graft its
992 /// outer leaf, a model its derivation — and forward the rest
993 /// inward, so resolution is nearest-ancestor per component.
994 fn provenance(&self, _node: NodeId) -> Provenance {
995 Provenance::default()
996 }
997
998 /// Whether `node` is an id this adapter minted — the guard
999 /// behind the node-id anchor `((!N))`, which may name an id
1000 /// from an earlier run (ids are minted deterministically: the
1001 /// same source, the same ids). The default probes the id
1002 /// through `parent` and `name` and treats a panic as absence,
1003 /// so an adapter that indexes by id needs no override; one that
1004 /// can answer cheaply should.
1005 fn has_node(&self, node: NodeId) -> bool {
1006 if node == self.root() {
1007 return true;
1008 }
1009 // An adapter that indexes its arena panics on a foreign id;
1010 // the probe catches that. The panic hook is installed once
1011 // for the process and silenced per thread while probing —
1012 // never swapped, so other threads keep theirs.
1013 probe::silently(|| self.parent(node).is_some() || self.name(node).is_some())
1014 }
1015
1016 /// The data provenance of `node` as a list — every source the
1017 /// substrate records for the datum, and how many it elides.
1018 /// The default is the singular answer as a one-entry list; an
1019 /// adapter whose substrate records several (kaiv's `?a;b;+N`)
1020 /// overrides it, and a wrapper layers each entry.
1021 fn provenance_list(&self, node: NodeId) -> ProvenanceList {
1022 ProvenanceList::single(self.provenance(node))
1023 }
1024
1025 /// The scale of a unit expression — (factor, canonical SI-base
1026 /// expansion) — for the unital reading's criterion text (spec:
1027 /// The Quantital Fragment). The default answers from the
1028 /// engine's frozen built-in table; a unit-aware adapter (kaiv)
1029 /// overrides it to include the mounted document's own custom
1030 /// units, so `[::range < '50kellicam']` resolves through the
1031 /// document's `.!units` imports.
1032 fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
1033 crate::quantity::scale_expr(expr)
1034 }
1035}
1036
1037/// Forwards every [`AstAdapter`] method to a wrapped adapter — the
1038/// one definition behind the wrappers (the boxed carrier, the
1039/// decoding layer, the mount and model wrappers), so a method added to the
1040/// trait is forwarded everywhere at once instead of silently
1041/// falling back to the trait default in whichever wrapper the
1042/// hand-written list missed.
1043///
1044/// The first argument names the wrapped adapter: a field (`inner`,
1045/// `0`) reached as `self.FIELD`, or `*` for a `Box<dyn AstAdapter>`
1046/// forwarding through `**self`. The optional `except` list names the
1047/// methods the wrapper defines itself, right after the invocation:
1048///
1049/// ```ignore
1050/// impl<A: AstAdapter> AstAdapter for Bounded<'_, A> {
1051/// quarb::forward_ast_adapter!(inner; except quantifier_bound);
1052/// fn quantifier_bound(&self) -> usize {
1053/// self.bound
1054/// }
1055/// }
1056/// ```
1057///
1058/// The method table inside the macro is checked against the trait
1059/// by `forwarding::the_macro_lists_every_trait_method`, and each
1060/// shipped wrapper against a probe adapter by
1061/// `forwarding::every_wrapper_forwards_every_method`.
1062#[macro_export]
1063macro_rules! forward_ast_adapter {
1064 ($inner:tt $(; except $($except:ident),+ $(,)?)? $(;)?) => {
1065 $crate::forward_ast_adapter!(@each [$inner] [$($($except)+)?]
1066 // Every method of `AstAdapter`, in the trait's order; the
1067 // coverage test reads this list from the source.
1068 root children name parent traits property properties
1069 walks_for_references can_refer_to reference_properties
1070 resolve_needs_label reverse_label tree_identities
1071 property_names resolves_references index_lookup
1072 index_referrers non_tree_nodes child_host children_named
1073 answers_to has_trait default_value metadata
1074 aliased_metadata links backlinks resolve external_ref
1075 resolve_fragment ref_property ref_label link_property
1076 document_by_ref reverse_resolve descendants_named
1077 descendants_where select select_plan estimate count
1078 prefetch_properties
1079 prefetch_links quantifier_bound
1080 provenance has_node
1081 provenance_list unit_scale
1082 );
1083 };
1084 (@each [$inner:tt] $ex:tt $($m:ident)*) => {
1085 $( $crate::forward_ast_adapter!(@unless [$inner] $ex $m); )*
1086 };
1087
1088 // The excepted method heads the list: emit nothing. One arm per
1089 // method, since a macro cannot compare two captured identifiers.
1090 (@unless [$i:tt] [root $($r:ident)*] root) => {};
1091 (@unless [$i:tt] [children $($r:ident)*] children) => {};
1092 (@unless [$i:tt] [name $($r:ident)*] name) => {};
1093 (@unless [$i:tt] [parent $($r:ident)*] parent) => {};
1094 (@unless [$i:tt] [traits $($r:ident)*] traits) => {};
1095 (@unless [$i:tt] [property $($r:ident)*] property) => {};
1096 (@unless [$i:tt] [properties $($r:ident)*] properties) => {};
1097 (@unless [$i:tt] [walks_for_references $($r:ident)*] walks_for_references) => {};
1098 (@unless [$i:tt] [can_refer_to $($r:ident)*] can_refer_to) => {};
1099 (@unless [$i:tt] [reference_properties $($r:ident)*] reference_properties) => {};
1100 (@unless [$i:tt] [resolve_needs_label $($r:ident)*] resolve_needs_label) => {};
1101 (@unless [$i:tt] [reverse_label $($r:ident)*] reverse_label) => {};
1102 (@unless [$i:tt] [tree_identities $($r:ident)*] tree_identities) => {};
1103 (@unless [$i:tt] [property_names $($r:ident)*] property_names) => {};
1104 (@unless [$i:tt] [resolves_references $($r:ident)*] resolves_references) => {};
1105 (@unless [$i:tt] [index_lookup $($r:ident)*] index_lookup) => {};
1106 (@unless [$i:tt] [index_referrers $($r:ident)*] index_referrers) => {};
1107 (@unless [$i:tt] [non_tree_nodes $($r:ident)*] non_tree_nodes) => {};
1108 (@unless [$i:tt] [child_host $($r:ident)*] child_host) => {};
1109 (@unless [$i:tt] [children_named $($r:ident)*] children_named) => {};
1110 (@unless [$i:tt] [answers_to $($r:ident)*] answers_to) => {};
1111 (@unless [$i:tt] [has_trait $($r:ident)*] has_trait) => {};
1112 (@unless [$i:tt] [default_value $($r:ident)*] default_value) => {};
1113 (@unless [$i:tt] [metadata $($r:ident)*] metadata) => {};
1114 (@unless [$i:tt] [aliased_metadata $($r:ident)*] aliased_metadata) => {};
1115 (@unless [$i:tt] [links $($r:ident)*] links) => {};
1116 (@unless [$i:tt] [backlinks $($r:ident)*] backlinks) => {};
1117 (@unless [$i:tt] [resolve $($r:ident)*] resolve) => {};
1118 (@unless [$i:tt] [external_ref $($r:ident)*] external_ref) => {};
1119 (@unless [$i:tt] [resolve_fragment $($r:ident)*] resolve_fragment) => {};
1120 (@unless [$i:tt] [ref_property $($r:ident)*] ref_property) => {};
1121 (@unless [$i:tt] [ref_label $($r:ident)*] ref_label) => {};
1122 (@unless [$i:tt] [link_property $($r:ident)*] link_property) => {};
1123 (@unless [$i:tt] [document_by_ref $($r:ident)*] document_by_ref) => {};
1124 (@unless [$i:tt] [reverse_resolve $($r:ident)*] reverse_resolve) => {};
1125 (@unless [$i:tt] [descendants_named $($r:ident)*] descendants_named) => {};
1126 (@unless [$i:tt] [descendants_where $($r:ident)*] descendants_where) => {};
1127 (@unless [$i:tt] [select $($r:ident)*] select) => {};
1128 (@unless [$i:tt] [select_plan $($r:ident)*] select_plan) => {};
1129 (@unless [$i:tt] [estimate $($r:ident)*] estimate) => {};
1130 (@unless [$i:tt] [count $($r:ident)*] count) => {};
1131 (@unless [$i:tt] [prefetch_properties $($r:ident)*] prefetch_properties) => {};
1132 (@unless [$i:tt] [prefetch_links $($r:ident)*] prefetch_links) => {};
1133 (@unless [$i:tt] [quantifier_bound $($r:ident)*] quantifier_bound) => {};
1134 (@unless [$i:tt] [provenance $($r:ident)*] provenance) => {};
1135 (@unless [$i:tt] [has_node $($r:ident)*] has_node) => {};
1136 (@unless [$i:tt] [provenance_list $($r:ident)*] provenance_list) => {};
1137 (@unless [$i:tt] [unit_scale $($r:ident)*] unit_scale) => {};
1138 // Another method heads the list: look further down.
1139 (@unless [$i:tt] [$x:ident $($r:ident)*] $m:ident) => {
1140 $crate::forward_ast_adapter!(@unless [$i] [$($r)*] $m);
1141 };
1142 // Not excepted: forward.
1143 (@unless [$i:tt] [] $m:ident) => {
1144 $crate::forward_ast_adapter!(@fwd [$i] $m);
1145 };
1146
1147 // The wrapped adapter, as an expression on `self`.
1148 (@target $s:ident *) => { (**$s) };
1149 (@target $s:ident $f:tt) => { $s.$f };
1150
1151 (@fwd [$i:tt] root) => {
1152 fn root(&self) -> $crate::NodeId {
1153 $crate::forward_ast_adapter!(@target self $i).root()
1154 }
1155 };
1156 (@fwd [$i:tt] children) => {
1157 fn children(&self, node: $crate::NodeId) -> Vec<$crate::NodeId> {
1158 $crate::forward_ast_adapter!(@target self $i).children(node)
1159 }
1160 };
1161 (@fwd [$i:tt] name) => {
1162 fn name(&self, node: $crate::NodeId) -> Option<String> {
1163 $crate::forward_ast_adapter!(@target self $i).name(node)
1164 }
1165 };
1166 (@fwd [$i:tt] parent) => {
1167 fn parent(&self, node: $crate::NodeId) -> Option<$crate::NodeId> {
1168 $crate::forward_ast_adapter!(@target self $i).parent(node)
1169 }
1170 };
1171 (@fwd [$i:tt] traits) => {
1172 fn traits(&self, node: $crate::NodeId) -> Vec<String> {
1173 $crate::forward_ast_adapter!(@target self $i).traits(node)
1174 }
1175 };
1176 (@fwd [$i:tt] property) => {
1177 fn property(&self, node: $crate::NodeId, name: &str) -> Option<$crate::Value> {
1178 $crate::forward_ast_adapter!(@target self $i).property(node, name)
1179 }
1180 };
1181 (@fwd [$i:tt] properties) => {
1182 fn properties(&self, node: $crate::NodeId) -> Vec<(String, $crate::Value)> {
1183 $crate::forward_ast_adapter!(@target self $i).properties(node)
1184 }
1185 };
1186 (@fwd [$i:tt] walks_for_references) => {
1187 fn walks_for_references(&self) -> bool {
1188 $crate::forward_ast_adapter!(@target self $i).walks_for_references()
1189 }
1190 };
1191 (@fwd [$i:tt] can_refer_to) => {
1192 fn can_refer_to(&self, source: $crate::NodeId, target: $crate::NodeId) -> Option<bool> {
1193 $crate::forward_ast_adapter!(@target self $i).can_refer_to(source, target)
1194 }
1195 };
1196 (@fwd [$i:tt] reference_properties) => {
1197 fn reference_properties(
1198 &self,
1199 source: $crate::NodeId,
1200 target: $crate::NodeId,
1201 ) -> Vec<String> {
1202 $crate::forward_ast_adapter!(@target self $i).reference_properties(source, target)
1203 }
1204 };
1205 (@fwd [$i:tt] resolve_needs_label) => {
1206 fn resolve_needs_label(&self, node: $crate::NodeId, property: &str) -> bool {
1207 $crate::forward_ast_adapter!(@target self $i).resolve_needs_label(node, property)
1208 }
1209 };
1210 (@fwd [$i:tt] reverse_label) => {
1211 fn reverse_label(&self, target: $crate::NodeId) -> Option<String> {
1212 $crate::forward_ast_adapter!(@target self $i).reverse_label(target)
1213 }
1214 };
1215 (@fwd [$i:tt] tree_identities) => {
1216 fn tree_identities(&self) -> Vec<(String, $crate::NodeId)> {
1217 $crate::forward_ast_adapter!(@target self $i).tree_identities()
1218 }
1219 };
1220 (@fwd [$i:tt] property_names) => {
1221 fn property_names(&self, node: $crate::NodeId) -> Vec<String> {
1222 $crate::forward_ast_adapter!(@target self $i).property_names(node)
1223 }
1224 };
1225 (@fwd [$i:tt] resolves_references) => {
1226 fn resolves_references(&self) -> bool {
1227 $crate::forward_ast_adapter!(@target self $i).resolves_references()
1228 }
1229 };
1230 (@fwd [$i:tt] index_lookup) => {
1231 fn index_lookup(
1232 &self,
1233 scope: &str,
1234 property: &str,
1235 value: &$crate::Value,
1236 ) -> Option<Vec<$crate::NodeId>> {
1237 $crate::forward_ast_adapter!(@target self $i).index_lookup(scope, property, value)
1238 }
1239 };
1240 (@fwd [$i:tt] index_referrers) => {
1241 fn index_referrers(
1242 &self,
1243 scope: &str,
1244 property: &str,
1245 label: Option<&str>,
1246 target: $crate::NodeId,
1247 ) -> Option<Vec<$crate::NodeId>> {
1248 $crate::forward_ast_adapter!(@target self $i)
1249 .index_referrers(scope, property, label, target)
1250 }
1251 };
1252 (@fwd [$i:tt] non_tree_nodes) => {
1253 fn non_tree_nodes(&self) -> Vec<$crate::NodeId> {
1254 $crate::forward_ast_adapter!(@target self $i).non_tree_nodes()
1255 }
1256 };
1257 (@fwd [$i:tt] child_host) => {
1258 fn child_host(&self, node: $crate::NodeId) -> $crate::NodeId {
1259 $crate::forward_ast_adapter!(@target self $i).child_host(node)
1260 }
1261 };
1262 (@fwd [$i:tt] children_named) => {
1263 fn children_named(&self, node: $crate::NodeId, name: &str) -> Vec<$crate::NodeId> {
1264 $crate::forward_ast_adapter!(@target self $i).children_named(node, name)
1265 }
1266 };
1267 (@fwd [$i:tt] answers_to) => {
1268 fn answers_to(&self, node: $crate::NodeId, name: &str) -> bool {
1269 $crate::forward_ast_adapter!(@target self $i).answers_to(node, name)
1270 }
1271 };
1272 (@fwd [$i:tt] has_trait) => {
1273 fn has_trait(&self, node: $crate::NodeId, name: &str) -> bool {
1274 $crate::forward_ast_adapter!(@target self $i).has_trait(node, name)
1275 }
1276 };
1277 (@fwd [$i:tt] default_value) => {
1278 fn default_value(&self, node: $crate::NodeId) -> Option<$crate::Value> {
1279 $crate::forward_ast_adapter!(@target self $i).default_value(node)
1280 }
1281 };
1282 (@fwd [$i:tt] metadata) => {
1283 fn metadata(&self, node: $crate::NodeId, key: &str) -> Option<$crate::Value> {
1284 $crate::forward_ast_adapter!(@target self $i).metadata(node, key)
1285 }
1286 };
1287 (@fwd [$i:tt] aliased_metadata) => {
1288 fn aliased_metadata(&self, node: $crate::NodeId) -> &'static [&'static str] {
1289 $crate::forward_ast_adapter!(@target self $i).aliased_metadata(node)
1290 }
1291 };
1292 (@fwd [$i:tt] links) => {
1293 fn links(&self, node: $crate::NodeId) -> Vec<(String, $crate::NodeId)> {
1294 $crate::forward_ast_adapter!(@target self $i).links(node)
1295 }
1296 };
1297 (@fwd [$i:tt] backlinks) => {
1298 fn backlinks(&self, node: $crate::NodeId) -> Vec<(String, $crate::NodeId)> {
1299 $crate::forward_ast_adapter!(@target self $i).backlinks(node)
1300 }
1301 };
1302 (@fwd [$i:tt] resolve) => {
1303 fn resolve(
1304 &self,
1305 node: $crate::NodeId,
1306 property: &str,
1307 hint: Option<&str>,
1308 ) -> Option<$crate::NodeId> {
1309 $crate::forward_ast_adapter!(@target self $i).resolve(node, property, hint)
1310 }
1311 };
1312 (@fwd [$i:tt] external_ref) => {
1313 fn external_ref(
1314 &self,
1315 node: $crate::NodeId,
1316 property: &str,
1317 hint: Option<&str>,
1318 ) -> Option<$crate::ExternalRef> {
1319 $crate::forward_ast_adapter!(@target self $i).external_ref(node, property, hint)
1320 }
1321 };
1322 (@fwd [$i:tt] resolve_fragment) => {
1323 fn resolve_fragment(&self, node: $crate::NodeId, fragment: &str) -> Option<$crate::NodeId> {
1324 $crate::forward_ast_adapter!(@target self $i).resolve_fragment(node, fragment)
1325 }
1326 };
1327 (@fwd [$i:tt] ref_property) => {
1328 fn ref_property(&self, node: $crate::NodeId) -> Option<String> {
1329 $crate::forward_ast_adapter!(@target self $i).ref_property(node)
1330 }
1331 };
1332 (@fwd [$i:tt] ref_label) => {
1333 fn ref_label(&self, node: $crate::NodeId, property: &str) -> Option<String> {
1334 $crate::forward_ast_adapter!(@target self $i).ref_label(node, property)
1335 }
1336 };
1337 (@fwd [$i:tt] link_property) => {
1338 fn link_property(
1339 &self,
1340 source: $crate::NodeId,
1341 label: &str,
1342 target: $crate::NodeId,
1343 name: &str,
1344 ) -> Option<$crate::Value> {
1345 $crate::forward_ast_adapter!(@target self $i).link_property(source, label, target, name)
1346 }
1347 };
1348 (@fwd [$i:tt] document_by_ref) => {
1349 fn document_by_ref(&self, id: &str) -> Option<$crate::NodeId> {
1350 $crate::forward_ast_adapter!(@target self $i).document_by_ref(id)
1351 }
1352 };
1353 (@fwd [$i:tt] reverse_resolve) => {
1354 fn reverse_resolve(
1355 &self,
1356 node: $crate::NodeId,
1357 property: Option<&str>,
1358 hint: Option<&str>,
1359 ) -> Option<Vec<$crate::NodeId>> {
1360 $crate::forward_ast_adapter!(@target self $i).reverse_resolve(node, property, hint)
1361 }
1362 };
1363 (@fwd [$i:tt] descendants_named) => {
1364 fn descendants_named(
1365 &self,
1366 node: $crate::NodeId,
1367 name: &str,
1368 ) -> Option<Vec<($crate::NodeId, usize)>> {
1369 $crate::forward_ast_adapter!(@target self $i).descendants_named(node, name)
1370 }
1371 };
1372 (@fwd [$i:tt] descendants_where) => {
1373 fn descendants_where(
1374 &self,
1375 node: $crate::NodeId,
1376 name: &str,
1377 property: &str,
1378 value: &$crate::Value,
1379 ) -> Option<Vec<($crate::NodeId, usize)>> {
1380 $crate::forward_ast_adapter!(@target self $i)
1381 .descendants_where(node, name, property, value)
1382 }
1383 };
1384 (@fwd [$i:tt] select) => {
1385 fn select(
1386 &self,
1387 from: $crate::NodeId,
1388 plan: &$crate::adapter::StepPlan<'_>,
1389 ) -> Option<$crate::adapter::Selection> {
1390 $crate::forward_ast_adapter!(@target self $i).select(from, plan)
1391 }
1392 };
1393 (@fwd [$i:tt] select_plan) => {
1394 fn select_plan(
1395 &self,
1396 scope: &[&str],
1397 plan: &$crate::adapter::StepPlan<'_>,
1398 ) -> Option<Vec<bool>> {
1399 $crate::forward_ast_adapter!(@target self $i).select_plan(scope, plan)
1400 }
1401 };
1402 (@fwd [$i:tt] estimate) => {
1403 fn estimate(
1404 &self,
1405 scope: &[&str],
1406 plan: &$crate::adapter::StepPlan<'_>,
1407 ) -> Option<$crate::adapter::Estimate> {
1408 $crate::forward_ast_adapter!(@target self $i).estimate(scope, plan)
1409 }
1410 };
1411 (@fwd [$i:tt] count) => {
1412 fn count(&self, from: $crate::NodeId, plan: &$crate::adapter::StepPlan<'_>) -> Option<usize> {
1413 $crate::forward_ast_adapter!(@target self $i).count(from, plan)
1414 }
1415 };
1416 (@fwd [$i:tt] prefetch_properties) => {
1417 fn prefetch_properties(&self, nodes: &[$crate::NodeId], names: &[&str]) {
1418 $crate::forward_ast_adapter!(@target self $i).prefetch_properties(nodes, names)
1419 }
1420 };
1421 (@fwd [$i:tt] prefetch_links) => {
1422 fn prefetch_links(&self, nodes: &[$crate::NodeId], dir: $crate::LinkDir) {
1423 $crate::forward_ast_adapter!(@target self $i).prefetch_links(nodes, dir)
1424 }
1425 };
1426 (@fwd [$i:tt] quantifier_bound) => {
1427 fn quantifier_bound(&self) -> usize {
1428 $crate::forward_ast_adapter!(@target self $i).quantifier_bound()
1429 }
1430 };
1431 (@fwd [$i:tt] provenance) => {
1432 fn provenance(&self, node: $crate::NodeId) -> $crate::Provenance {
1433 $crate::forward_ast_adapter!(@target self $i).provenance(node)
1434 }
1435 };
1436 (@fwd [$i:tt] has_node) => {
1437 fn has_node(&self, node: $crate::NodeId) -> bool {
1438 $crate::forward_ast_adapter!(@target self $i).has_node(node)
1439 }
1440 };
1441 (@fwd [$i:tt] provenance_list) => {
1442 fn provenance_list(&self, node: $crate::NodeId) -> $crate::ProvenanceList {
1443 $crate::forward_ast_adapter!(@target self $i).provenance_list(node)
1444 }
1445 };
1446 (@fwd [$i:tt] unit_scale) => {
1447 fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
1448 $crate::forward_ast_adapter!(@target self $i).unit_scale(expr)
1449 }
1450 };
1451}
1452
1453impl AstAdapter for Box<dyn AstAdapter> {
1454 crate::forward_ast_adapter!(*);
1455}
1456
1457#[cfg(test)]
1458mod forwarding {
1459 use super::*;
1460
1461 /// The names of the `fn` items at `indent` within `block`.
1462 fn fn_names(block: &str, indent: &str) -> Vec<String> {
1463 block
1464 .lines()
1465 .filter_map(|l| l.strip_prefix(indent)?.strip_prefix("fn "))
1466 .map(|rest| {
1467 rest.split(|c: char| !(c.is_alphanumeric() || c == '_'))
1468 .next()
1469 .unwrap()
1470 .to_string()
1471 })
1472 .collect()
1473 }
1474
1475 /// The macro's method table is the trait's method list — a
1476 /// method added to `AstAdapter` without a forwarding arm would
1477 /// otherwise answer with the trait default inside every wrapper.
1478 #[test]
1479 fn the_macro_lists_every_trait_method() {
1480 let src = include_str!("adapter.rs");
1481 let trait_src = &src[src.find("pub trait AstAdapter {").unwrap()..];
1482 let trait_src = &trait_src[..trait_src.find("\n}\n").unwrap()];
1483 let in_trait = fn_names(trait_src, " ");
1484
1485 let table = &src[src
1486 .find("coverage test reads this list from the source.")
1487 .unwrap()..];
1488 let table = &table[table.find('\n').unwrap()..table.find(");").unwrap()];
1489 let listed: Vec<String> = table.split_whitespace().map(str::to_string).collect();
1490
1491 assert_eq!(
1492 listed, in_trait,
1493 "the forwarding table drifted from the trait"
1494 );
1495
1496 let macro_src = &src[src.find("macro_rules! forward_ast_adapter").unwrap()..];
1497 let macro_src = ¯o_src[..macro_src.find("\n}\n").unwrap()];
1498 for m in &in_trait {
1499 assert!(
1500 macro_src.contains(&format!(
1501 "(@unless [$i:tt] [{m} $($r:ident)*] {m}) => {{}};"
1502 )),
1503 "no except arm for {m}"
1504 );
1505 assert!(
1506 macro_src.contains(&format!("(@fwd [$i:tt] {m}) => {{")),
1507 "no forwarding arm for {m}"
1508 );
1509 }
1510 }
1511
1512 /// Answers every method distinctively, so a wrapper that
1513 /// forwards one method to another (or to the default) is caught.
1514 struct Probe;
1515
1516 fn n(k: u64) -> NodeId {
1517 NodeId(k)
1518 }
1519
1520 fn int(v: &Value) -> u64 {
1521 match v {
1522 Value::Int(i) => *i as u64,
1523 _ => 0,
1524 }
1525 }
1526
1527 impl AstAdapter for Probe {
1528 fn root(&self) -> NodeId {
1529 n(1)
1530 }
1531 fn children(&self, node: NodeId) -> Vec<NodeId> {
1532 vec![n(node.0 * 10)]
1533 }
1534 fn name(&self, node: NodeId) -> Option<String> {
1535 Some(format!("name{}", node.0))
1536 }
1537 fn parent(&self, node: NodeId) -> Option<NodeId> {
1538 Some(n(node.0 + 100))
1539 }
1540 fn traits(&self, node: NodeId) -> Vec<String> {
1541 vec![format!("trait{}", node.0)]
1542 }
1543 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
1544 Some(Value::Str(format!("prop:{}:{name}", node.0)))
1545 }
1546 fn properties(&self, node: NodeId) -> Vec<(String, Value)> {
1547 vec![(format!("p{}", node.0), Value::Int(node.0 as i64))]
1548 }
1549 fn walks_for_references(&self) -> bool {
1550 true
1551 }
1552 fn can_refer_to(&self, source: NodeId, target: NodeId) -> Option<bool> {
1553 Some(source.0 < target.0)
1554 }
1555 fn reference_properties(&self, source: NodeId, target: NodeId) -> Vec<String> {
1556 vec![format!("rp{}-{}", source.0, target.0)]
1557 }
1558 fn resolve_needs_label(&self, node: NodeId, property: &str) -> bool {
1559 node.0 == 7 && property == "x"
1560 }
1561 fn reverse_label(&self, target: NodeId) -> Option<String> {
1562 Some(format!("rl{}", target.0))
1563 }
1564 fn tree_identities(&self) -> Vec<(String, NodeId)> {
1565 vec![("tree".into(), n(42))]
1566 }
1567 fn property_names(&self, node: NodeId) -> Vec<String> {
1568 vec![format!("pn{}", node.0)]
1569 }
1570 fn resolves_references(&self) -> bool {
1571 false
1572 }
1573 fn index_lookup(&self, scope: &str, property: &str, value: &Value) -> Option<Vec<NodeId>> {
1574 Some(vec![
1575 n((scope.len() + property.len()) as u64),
1576 n(int(value)),
1577 ])
1578 }
1579 fn index_referrers(
1580 &self,
1581 scope: &str,
1582 property: &str,
1583 label: Option<&str>,
1584 target: NodeId,
1585 ) -> Option<Vec<NodeId>> {
1586 Some(vec![n(
1587 (scope.len() + property.len() + label.map_or(0, str::len)) as u64 + target.0,
1588 )])
1589 }
1590 fn non_tree_nodes(&self) -> Vec<NodeId> {
1591 vec![n(99)]
1592 }
1593 fn child_host(&self, node: NodeId) -> NodeId {
1594 n(node.0 + 1000)
1595 }
1596 fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
1597 vec![n(node.0 + name.len() as u64)]
1598 }
1599 fn answers_to(&self, node: NodeId, name: &str) -> bool {
1600 node.0 == 3 && name == "alias"
1601 }
1602 fn has_trait(&self, node: NodeId, name: &str) -> bool {
1603 node.0 == 4 && name == "talias"
1604 }
1605 fn default_value(&self, node: NodeId) -> Option<Value> {
1606 Some(Value::Int(-(node.0 as i64)))
1607 }
1608 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
1609 Some(Value::Str(format!("meta:{}:{key}", node.0)))
1610 }
1611 fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
1612 if node.0 == 5 { &["size"] } else { &["other"] }
1613 }
1614 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
1615 vec![("link".into(), n(node.0 + 2))]
1616 }
1617 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
1618 vec![("back".into(), n(node.0 + 3))]
1619 }
1620 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
1621 Some(n(node.0
1622 + property.len() as u64
1623 + hint.map_or(0, |h| h.len() as u64)))
1624 }
1625 fn external_ref(
1626 &self,
1627 node: NodeId,
1628 property: &str,
1629 hint: Option<&str>,
1630 ) -> Option<ExternalRef> {
1631 Some(ExternalRef {
1632 document: format!("doc{}:{property}", node.0),
1633 within: hint.map(str::to_string),
1634 })
1635 }
1636 fn resolve_fragment(&self, node: NodeId, fragment: &str) -> Option<NodeId> {
1637 Some(n(node.0 + fragment.len() as u64 + 7))
1638 }
1639 fn ref_property(&self, node: NodeId) -> Option<String> {
1640 Some(format!("href{}", node.0))
1641 }
1642 fn ref_label(&self, node: NodeId, property: &str) -> Option<String> {
1643 Some(format!("rel{}:{property}", node.0))
1644 }
1645 fn link_property(
1646 &self,
1647 source: NodeId,
1648 label: &str,
1649 target: NodeId,
1650 name: &str,
1651 ) -> Option<Value> {
1652 Some(Value::Str(format!(
1653 "{}-{label}-{}-{name}",
1654 source.0, target.0
1655 )))
1656 }
1657 fn document_by_ref(&self, id: &str) -> Option<NodeId> {
1658 Some(n(id.len() as u64 + 50))
1659 }
1660 fn reverse_resolve(
1661 &self,
1662 node: NodeId,
1663 property: Option<&str>,
1664 hint: Option<&str>,
1665 ) -> Option<Vec<NodeId>> {
1666 Some(vec![n(node.0
1667 + property.map_or(0, |p| p.len() as u64) * 2
1668 + hint.map_or(0, |h| h.len() as u64) * 3)])
1669 }
1670 fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
1671 Some(vec![(n(node.0 + 9), name.len())])
1672 }
1673 fn descendants_where(
1674 &self,
1675 node: NodeId,
1676 name: &str,
1677 property: &str,
1678 value: &Value,
1679 ) -> Option<Vec<(NodeId, usize)>> {
1680 Some(vec![(n(node.0 + int(value)), name.len() + property.len())])
1681 }
1682 fn select(&self, from: NodeId, plan: &StepPlan<'_>) -> Option<Selection> {
1683 Some(Selection {
1684 nodes: vec![(n(from.0 + plan.conjuncts.len() as u64 + 1), 3)],
1685 applied: vec![true; plan.conjuncts.len()],
1686 })
1687 }
1688 fn select_plan(&self, scope: &[&str], plan: &StepPlan<'_>) -> Option<Vec<bool>> {
1689 Some(vec![scope.len() == 1; plan.conjuncts.len()])
1690 }
1691 fn estimate(&self, scope: &[&str], plan: &StepPlan<'_>) -> Option<Estimate> {
1692 Some(Estimate {
1693 rows: (scope.len() as u64, plan.conjuncts.len() as u64 + 9),
1694 bytes: None,
1695 requests: Some(1),
1696 latency: Latency::Local,
1697 confidence: Confidence::Bound,
1698 })
1699 }
1700 fn count(&self, from: NodeId, plan: &StepPlan<'_>) -> Option<usize> {
1701 Some(from.0 as usize + plan.conjuncts.len() + 40)
1702 }
1703 fn prefetch_properties(&self, nodes: &[NodeId], names: &[&str]) {
1704 PREFETCHED.with(|p| p.set(nodes.len() * 100 + names.len()));
1705 }
1706 fn prefetch_links(&self, nodes: &[NodeId], dir: LinkDir) {
1707 PREFETCHED.with(|p| p.set(nodes.len() * 10 + matches!(dir, LinkDir::In) as usize));
1708 }
1709 fn quantifier_bound(&self) -> usize {
1710 77
1711 }
1712 fn provenance(&self, node: NodeId) -> Provenance {
1713 Provenance {
1714 source: Some(format!("src{}", node.0)),
1715 ..Default::default()
1716 }
1717 }
1718 fn has_node(&self, node: NodeId) -> bool {
1719 node.0 == 8
1720 }
1721 fn provenance_list(&self, node: NodeId) -> ProvenanceList {
1722 ProvenanceList {
1723 entries: vec![self.provenance(node)],
1724 elided: node.0 as u32,
1725 }
1726 }
1727 fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
1728 Some((expr.len() as f64, format!("scale:{expr}")))
1729 }
1730 }
1731
1732 thread_local! {
1733 static PREFETCHED: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
1734 }
1735
1736 /// Every method of `w` answers as `Probe` does, except those in
1737 /// `except`, which the wrapper defines itself.
1738 fn assert_forwards(w: &dyn AstAdapter, except: &[&str]) {
1739 let p = Probe;
1740 let v = Value::Int(6);
1741 macro_rules! same {
1742 ($m:ident $(, $a:expr)*) => {
1743 if !except.contains(&stringify!($m)) {
1744 assert_eq!(w.$m($($a),*), p.$m($($a),*), stringify!($m));
1745 }
1746 };
1747 }
1748 same!(root);
1749 same!(children, n(2));
1750 same!(name, n(2));
1751 same!(parent, n(2));
1752 same!(traits, n(2));
1753 same!(property, n(2), "k");
1754 same!(properties, n(2));
1755 same!(walks_for_references);
1756 same!(can_refer_to, n(2), n(3));
1757 same!(reference_properties, n(2), n(3));
1758 same!(resolve_needs_label, n(7), "x");
1759 same!(reverse_label, n(2));
1760 same!(tree_identities);
1761 same!(property_names, n(2));
1762 same!(resolves_references);
1763 same!(index_lookup, "s", "pp", &v);
1764 same!(index_referrers, "s", "pp", Some("lbl"), n(2));
1765 same!(non_tree_nodes);
1766 same!(child_host, n(2));
1767 same!(children_named, n(2), "abc");
1768 same!(answers_to, n(3), "alias");
1769 same!(has_trait, n(4), "talias");
1770 same!(default_value, n(2));
1771 same!(metadata, n(2), "key");
1772 same!(aliased_metadata, n(5));
1773 same!(links, n(2));
1774 same!(backlinks, n(2));
1775 same!(resolve, n(2), "pp", Some("h"));
1776 same!(external_ref, n(2), "pp", Some("frag"));
1777 same!(resolve_fragment, n(2), "frag");
1778 same!(ref_property, n(2));
1779 same!(ref_label, n(2), "pp");
1780 same!(link_property, n(2), "lbl", n(3), "nm");
1781 same!(document_by_ref, "id");
1782 same!(reverse_resolve, n(2), Some("pp"), Some("h"));
1783 same!(descendants_named, n(2), "abc");
1784 same!(descendants_where, n(2), "abc", "pp", &v);
1785 let plan = StepPlan {
1786 axis: PlanAxis::Descendant,
1787 name: Some("abc"),
1788 conjuncts: vec![Conjunct::Equals("pp", &v)],
1789 limit: None,
1790 order: None,
1791 };
1792 if !except.contains(&"select") {
1793 assert_eq!(
1794 w.select(n(2), &plan).map(|s| s.nodes),
1795 p.select(n(2), &plan).map(|s| s.nodes),
1796 "select"
1797 );
1798 }
1799 same!(count, n(2), &plan);
1800 same!(select_plan, &["t"], &plan);
1801 same!(estimate, &["t"], &plan);
1802 if !except.contains(&"prefetch_properties") {
1803 PREFETCHED.with(|p| p.set(0));
1804 w.prefetch_properties(&[n(1), n(2), n(3)], &["a", "b"]);
1805 assert_eq!(PREFETCHED.with(|p| p.get()), 302, "prefetch_properties");
1806 }
1807 if !except.contains(&"prefetch_links") {
1808 PREFETCHED.with(|c| c.set(0));
1809 w.prefetch_links(&[n(1), n(2)], LinkDir::In);
1810 assert_eq!(PREFETCHED.with(|c| c.get()), 21, "prefetch_links");
1811 }
1812 same!(quantifier_bound);
1813 same!(provenance, n(2));
1814 same!(has_node, n(8));
1815 same!(has_node, n(9));
1816 same!(provenance_list, n(2));
1817 same!(unit_scale, "km");
1818 }
1819
1820 #[test]
1821 fn every_wrapper_forwards_every_method() {
1822 let p = Probe;
1823 let b: Box<dyn AstAdapter> = Box::new(Probe);
1824 assert_forwards(&b, &[]);
1825
1826 let s = crate::source::Sourced {
1827 inner: Probe,
1828 encoding: "utf-8".into(),
1829 replaced: 0,
1830 };
1831 assert_forwards(&s, &["metadata"]);
1832 assert_eq!(s.metadata(n(2), "key"), p.metadata(n(2), "key"));
1833 assert_eq!(
1834 s.metadata(n(1), "encoding"),
1835 Some(Value::Str("utf-8".into()))
1836 );
1837 }
1838}