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    /// (wasm-safe — the playground uses it too).
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            model: None,
37        }
38    }
39
40    /// Attach a `--model` file: the session runs every query against
41    /// the enriched view.
42    pub fn with_model(mut self, model: Option<quarb_model::Model>) -> Self {
43        self.model = model;
44        self
45    }
46
47    /// A native executor that can re-materialize its source for a
48    /// `&N!` live reading.
49    #[cfg(feature = "native")]
50    pub fn with_respec(
51        doc: Doc,
52        now: (i64, u32),
53        allow_shell: bool,
54        specs: Vec<crate::MountSpec>,
55        opts: crate::Options,
56    ) -> Self {
57        Self {
58            doc,
59            now,
60            allow_shell,
61            respec: Some((specs, opts)),
62            model: None,
63        }
64    }
65}
66
67/// Run one query against a `Doc`, rendering its result to [`Cell`]s.
68fn run_doc(doc: &Doc, query: &str, now: (i64, u32), allow_shell: bool) -> anyhow::Result<Vec<Cell>> {
69    let result = doc
70        .run(query, now, allow_shell)
71        .map_err(|e| anyhow::anyhow!("{e}"))?;
72    Ok(match result {
73        QueryResult::Nodes(nodes) => nodes.into_iter().map(|n| Cell::Node(doc.render(n))).collect(),
74        QueryResult::Values(values) => values.into_iter().map(Cell::Value).collect(),
75    })
76}
77
78/// [`run_doc`], against a model-enriched view.
79fn run_doc_modeled(
80    doc: &Doc,
81    query: &str,
82    now: (i64, u32),
83    allow_shell: bool,
84    model: &quarb_model::Model,
85) -> anyhow::Result<Vec<Cell>> {
86    let result = doc
87        .run_modeled(query, now, allow_shell, model)
88        .map_err(|e| anyhow::anyhow!("{e}"))?;
89    Ok(match result {
90        QueryResult::Nodes(nodes) => nodes
91            .into_iter()
92            .map(|n| Cell::Node(doc.render_modeled(n, model)))
93            .collect(),
94        QueryResult::Values(values) => values.into_iter().map(Cell::Value).collect(),
95    })
96}
97
98impl Executor for LocalExecutor {
99    fn run(&self, query: &str) -> anyhow::Result<Vec<Cell>> {
100        if let Some(model) = &self.model {
101            return run_doc_modeled(&self.doc, query, self.now, self.allow_shell, model);
102        }
103        run_doc(&self.doc, query, self.now, self.allow_shell)
104    }
105
106    fn run_fresh(&self, query: &str) -> anyhow::Result<Vec<Cell>> {
107        #[cfg(feature = "native")]
108        if let Some((specs, opts)) = &self.respec {
109            let fresh = match specs.as_slice() {
110                [one] if one.name.is_none() => Doc::open(&one.path, opts)?,
111                many => Doc::mount_specs(many, opts)?,
112            };
113            return run_doc(&fresh, query, self.now, self.allow_shell);
114        }
115        self.run(query)
116    }
117}