1use crate::doc::Doc;
9use crate::{Cell, Executor};
10use quarb::QueryResult;
11
12pub struct LocalExecutor {
13 doc: Doc,
14 url_roots: std::collections::HashMap<String, quarb::NodeId>,
19 refs: std::cell::RefCell<Vec<String>>,
23 now: (i64, u32),
25 allow_shell: bool,
26 model: Option<quarb_model::Model>,
29 #[cfg(feature = "native")]
33 respec: Option<(Vec<crate::MountSpec>, crate::Options)>,
34}
35
36impl LocalExecutor {
37 pub fn new(doc: Doc, now: (i64, u32), allow_shell: bool) -> Self {
39 Self {
40 doc,
41 url_roots: Default::default(),
42 refs: Default::default(),
43 now,
44 allow_shell,
45 #[cfg(feature = "native")]
46 respec: None,
47 model: None,
48 }
49 }
50
51 pub fn with_url_roots(mut self, map: std::collections::HashMap<String, quarb::NodeId>) -> Self {
54 self.url_roots = map;
55 self
56 }
57
58 pub fn with_model(mut self, model: Option<quarb_model::Model>) -> Self {
61 self.model = model;
62 self
63 }
64
65 #[cfg(feature = "native")]
68 pub fn with_respec(
69 doc: Doc,
70 now: (i64, u32),
71 allow_shell: bool,
72 specs: Vec<crate::MountSpec>,
73 opts: crate::Options,
74 ) -> Self {
75 Self {
76 doc,
77 url_roots: Default::default(),
78 refs: Default::default(),
79 now,
80 allow_shell,
81 respec: Some((specs, opts)),
82 model: None,
83 }
84 }
85}
86
87fn run_doc(
89 doc: &Doc,
90 query: &str,
91 now: (i64, u32),
92 allow_shell: bool,
93) -> anyhow::Result<Vec<Cell>> {
94 let result = doc
95 .run(query, now, allow_shell)
96 .map_err(|e| anyhow::anyhow!("{e}"))?;
97 Ok(match result {
98 QueryResult::Nodes(nodes) => nodes
99 .into_iter()
100 .map(|n| Cell::Node(doc.render(n)))
101 .collect(),
102 QueryResult::Values(values) => values.into_iter().map(Cell::Value).collect(),
103 })
104}
105
106fn run_doc_modeled(
108 doc: &Doc,
109 query: &str,
110 now: (i64, u32),
111 allow_shell: bool,
112 model: &quarb_model::Model,
113) -> anyhow::Result<Vec<Cell>> {
114 let result = doc
115 .run_modeled(query, now, allow_shell, model)
116 .map_err(|e| anyhow::anyhow!("{e}"))?;
117 Ok(match result {
118 QueryResult::Nodes(nodes) => nodes
119 .into_iter()
120 .map(|n| Cell::Node(doc.render_modeled(n, model)))
121 .collect(),
122 QueryResult::Values(values) => values.into_iter().map(Cell::Value).collect(),
123 })
124}
125
126impl Executor for LocalExecutor {
127 fn run(&self, query: &str) -> anyhow::Result<Vec<Cell>> {
128 quarb::set_ref_targets(self.url_roots.clone());
129 let out = if let Some(model) = &self.model {
130 run_doc_modeled(&self.doc, query, self.now, self.allow_shell, model)
131 } else {
132 run_doc(&self.doc, query, self.now, self.allow_shell)
133 };
134 *self.refs.borrow_mut() = quarb::take_refs();
135 out
136 }
137
138 fn refs(&self) -> Vec<String> {
139 self.refs.borrow().clone()
140 }
141
142 fn run_fresh(&self, query: &str) -> anyhow::Result<Vec<Cell>> {
143 #[cfg(feature = "native")]
144 if let Some((specs, opts)) = &self.respec {
145 let fresh = match specs.as_slice() {
146 [one] if one.name.is_none() => Doc::open(&one.path, opts)?,
147 many => Doc::mount_specs(many, opts)?,
148 };
149 return run_doc(&fresh, query, self.now, self.allow_shell);
150 }
151 self.run(query)
152 }
153
154 fn complete(&self, text: &str, cursor: usize) -> Vec<quarb::complete::Candidate> {
155 quarb::complete::complete_with(text, cursor, self.doc.base_dyn())
156 }
157
158 fn export(&self, query: &str, kind: &str) -> anyhow::Result<String> {
159 if self.model.is_some() {
160 anyhow::bail!("rendered export over a --model session is not supported yet");
161 }
162 self.doc.export(query, self.now, self.allow_shell, kind)
163 }
164}