Skip to main content

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 = &macro_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}