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(
54 mut self,
55 map: std::collections::HashMap<String, quarb::NodeId>,
56 ) -> Self {
57 self.url_roots = map;
58 self
59 }
60
61 pub fn with_model(mut self, model: Option<quarb_model::Model>) -> Self {
64 self.model = model;
65 self
66 }
67
68 #[cfg(feature = "native")]
71 pub fn with_respec(
72 doc: Doc,
73 now: (i64, u32),
74 allow_shell: bool,
75 specs: Vec<crate::MountSpec>,
76 opts: crate::Options,
77 ) -> Self {
78 Self {
79 doc,
80 url_roots: Default::default(),
81 refs: Default::default(),
82 now,
83 allow_shell,
84 respec: Some((specs, opts)),
85 model: None,
86 }
87 }
88}
89
90fn run_doc(doc: &Doc, query: &str, now: (i64, u32), allow_shell: bool) -> anyhow::Result<Vec<Cell>> {
92 let result = doc
93 .run(query, now, allow_shell)
94 .map_err(|e| anyhow::anyhow!("{e}"))?;
95 Ok(match result {
96 QueryResult::Nodes(nodes) => nodes.into_iter().map(|n| Cell::Node(doc.render(n))).collect(),
97 QueryResult::Values(values) => values.into_iter().map(Cell::Value).collect(),
98 })
99}
100
101fn run_doc_modeled(
103 doc: &Doc,
104 query: &str,
105 now: (i64, u32),
106 allow_shell: bool,
107 model: &quarb_model::Model,
108) -> anyhow::Result<Vec<Cell>> {
109 let result = doc
110 .run_modeled(query, now, allow_shell, model)
111 .map_err(|e| anyhow::anyhow!("{e}"))?;
112 Ok(match result {
113 QueryResult::Nodes(nodes) => nodes
114 .into_iter()
115 .map(|n| Cell::Node(doc.render_modeled(n, model)))
116 .collect(),
117 QueryResult::Values(values) => values.into_iter().map(Cell::Value).collect(),
118 })
119}
120
121impl Executor for LocalExecutor {
122 fn run(&self, query: &str) -> anyhow::Result<Vec<Cell>> {
123 quarb::set_ref_targets(self.url_roots.clone());
124 let out = if let Some(model) = &self.model {
125 run_doc_modeled(&self.doc, query, self.now, self.allow_shell, model)
126 } else {
127 run_doc(&self.doc, query, self.now, self.allow_shell)
128 };
129 *self.refs.borrow_mut() = quarb::take_refs();
130 out
131 }
132
133 fn refs(&self) -> Vec<String> {
134 self.refs.borrow().clone()
135 }
136
137 fn run_fresh(&self, query: &str) -> anyhow::Result<Vec<Cell>> {
138 #[cfg(feature = "native")]
139 if let Some((specs, opts)) = &self.respec {
140 let fresh = match specs.as_slice() {
141 [one] if one.name.is_none() => Doc::open(&one.path, opts)?,
142 many => Doc::mount_specs(many, opts)?,
143 };
144 return run_doc(&fresh, query, self.now, self.allow_shell);
145 }
146 self.run(query)
147 }
148
149 fn complete(&self, text: &str, cursor: usize) -> Vec<quarb::complete::Candidate> {
150 quarb::complete::complete_with(text, cursor, self.doc.base_dyn())
151 }
152
153 fn export(&self, query: &str, kind: &str) -> anyhow::Result<String> {
154 if self.model.is_some() {
155 anyhow::bail!("rendered export over a --model session is not supported yet");
156 }
157 self.doc.export(query, self.now, self.allow_shell, kind)
158 }
159}