Skip to main content

quarb_session/
local.rs

1//! The in-process executor: materialize and query a [`Doc`] here.
2//!
3//! Serves both native (the full adapter fleet) and wasm (the
4//! text-format subset) — the two differ only in which `Doc` variants
5//! compile. A native executor may also carry its source spec, so a
6//! `&N!` live reading can re-open the source and see current data.
7
8use crate::doc::Doc;
9use crate::{Cell, Executor};
10use quarb::QueryResult;
11
12pub struct LocalExecutor {
13    doc: Doc,
14    /// The invocation instant `now()` denotes, bound once per session.
15    now: (i64, u32),
16    allow_shell: bool,
17    /// A `--model` file enriching the source with derived structure.
18    #[cfg(feature = "native")]
19    model: Option<quarb_model::Model>,
20    /// The source spec, kept so a `&N!` reading can re-materialize.
21    /// `None` when the source can't be re-opened (wasm pasted text,
22    /// which never drifts anyway).
23    #[cfg(feature = "native")]
24    respec: Option<(Vec<crate::MountSpec>, crate::Options)>,
25}
26
27impl LocalExecutor {
28    /// An executor over a fixed materialized `Doc` (no live re-read).
29    pub fn new(doc: Doc, now: (i64, u32), allow_shell: bool) -> Self {
30        Self {
31            doc,
32            now,
33            allow_shell,
34            #[cfg(feature = "native")]
35            respec: None,
36            #[cfg(feature = "native")]
37            model: None,
38        }
39    }
40
41    /// Attach a `--model` file: the session runs every query against
42    /// the enriched view.
43    #[cfg(feature = "native")]
44    pub fn with_model(mut self, model: Option<quarb_model::Model>) -> Self {
45        self.model = model;
46        self
47    }
48
49    /// A native executor that can re-materialize its source for a
50    /// `&N!` live reading.
51    #[cfg(feature = "native")]
52    pub fn with_respec(
53        doc: Doc,
54        now: (i64, u32),
55        allow_shell: bool,
56        specs: Vec<crate::MountSpec>,
57        opts: crate::Options,
58    ) -> Self {
59        Self {
60            doc,
61            now,
62            allow_shell,
63            respec: Some((specs, opts)),
64            #[cfg(feature = "native")]
65            model: None,
66        }
67    }
68}
69
70/// Run one query against a `Doc`, rendering its result to [`Cell`]s.
71fn run_doc(doc: &Doc, query: &str, now: (i64, u32), allow_shell: bool) -> anyhow::Result<Vec<Cell>> {
72    let result = doc
73        .run(query, now, allow_shell)
74        .map_err(|e| anyhow::anyhow!("{e}"))?;
75    Ok(match result {
76        QueryResult::Nodes(nodes) => nodes.into_iter().map(|n| Cell::Node(doc.render(n))).collect(),
77        QueryResult::Values(values) => values.into_iter().map(Cell::Value).collect(),
78    })
79}
80
81/// [`run_doc`], against a model-enriched view.
82#[cfg(feature = "native")]
83fn run_doc_modeled(
84    doc: &Doc,
85    query: &str,
86    now: (i64, u32),
87    allow_shell: bool,
88    model: &quarb_model::Model,
89) -> anyhow::Result<Vec<Cell>> {
90    let result = doc
91        .run_modeled(query, now, allow_shell, model)
92        .map_err(|e| anyhow::anyhow!("{e}"))?;
93    Ok(match result {
94        QueryResult::Nodes(nodes) => nodes
95            .into_iter()
96            .map(|n| Cell::Node(doc.render_modeled(n, model)))
97            .collect(),
98        QueryResult::Values(values) => values.into_iter().map(Cell::Value).collect(),
99    })
100}
101
102impl Executor for LocalExecutor {
103    fn run(&self, query: &str) -> anyhow::Result<Vec<Cell>> {
104        #[cfg(feature = "native")]
105        if let Some(model) = &self.model {
106            return run_doc_modeled(&self.doc, query, self.now, self.allow_shell, model);
107        }
108        run_doc(&self.doc, query, self.now, self.allow_shell)
109    }
110
111    fn run_fresh(&self, query: &str) -> anyhow::Result<Vec<Cell>> {
112        #[cfg(feature = "native")]
113        if let Some((specs, opts)) = &self.respec {
114            let fresh = match specs.as_slice() {
115                [one] if one.name.is_none() => Doc::open(&one.path, opts)?,
116                many => Doc::mount_specs(many, opts)?,
117            };
118            return run_doc(&fresh, query, self.now, self.allow_shell);
119        }
120        self.run(query)
121    }
122}