1use std::sync;
2
3use crate::error::Error;
4use crate::error::Result;
5use crate::model::{Object, ObjectView, Scalar, ScalarCow, Value, ValueCow, ValueView};
6
7use super::PartialStore;
8use super::Renderable;
9
10pub trait Runtime {
12 fn partials(&self) -> &dyn PartialStore;
14
15 fn name(&self) -> Option<crate::model::KStringRef<'_>>;
17
18 fn roots(&self) -> std::collections::BTreeSet<crate::model::KStringCow<'_>>;
20 fn try_get(&self, path: &[ScalarCow<'_>]) -> Option<ValueCow<'_>>;
22 fn get(&self, path: &[ScalarCow<'_>]) -> Result<ValueCow<'_>>;
24
25 fn set_global(
27 &self,
28 name: crate::model::KString,
29 val: crate::model::Value,
30 ) -> Option<crate::model::Value>;
31
32 fn set_index(&self, name: crate::model::KString, val: Value) -> Option<Value>;
34 fn get_index<'a>(&'a self, name: &str) -> Option<ValueCow<'a>>;
36
37 fn registers(&self) -> &Registers;
39}
40
41impl<R: Runtime + ?Sized> Runtime for &R {
42 fn partials(&self) -> &dyn super::PartialStore {
43 <R as Runtime>::partials(self)
44 }
45
46 fn name(&self) -> Option<crate::model::KStringRef<'_>> {
47 <R as Runtime>::name(self)
48 }
49
50 fn roots(&self) -> std::collections::BTreeSet<crate::model::KStringCow<'_>> {
51 <R as Runtime>::roots(self)
52 }
53
54 fn try_get(&self, path: &[ScalarCow<'_>]) -> Option<ValueCow<'_>> {
55 <R as Runtime>::try_get(self, path)
56 }
57
58 fn get(&self, path: &[ScalarCow<'_>]) -> Result<ValueCow<'_>> {
59 <R as Runtime>::get(self, path)
60 }
61
62 fn set_global(
63 &self,
64 name: crate::model::KString,
65 val: crate::model::Value,
66 ) -> Option<crate::model::Value> {
67 <R as Runtime>::set_global(self, name, val)
68 }
69
70 fn set_index(&self, name: crate::model::KString, val: Value) -> Option<Value> {
71 <R as Runtime>::set_index(self, name, val)
72 }
73
74 fn get_index<'a>(&'a self, name: &str) -> Option<ValueCow<'a>> {
75 <R as Runtime>::get_index(self, name)
76 }
77
78 fn registers(&self) -> &super::Registers {
79 <R as Runtime>::registers(self)
80 }
81}
82
83pub struct RuntimeBuilder<'g, 'p> {
85 globals: Option<&'g dyn ObjectView>,
86 partials: Option<&'p dyn PartialStore>,
87}
88
89impl<'c, 'g: 'c, 'p: 'c> RuntimeBuilder<'g, 'p> {
90 pub fn new() -> Self {
92 Self {
93 globals: None,
94 partials: None,
95 }
96 }
97
98 pub fn set_globals<'n>(self, values: &'n dyn ObjectView) -> RuntimeBuilder<'n, 'p> {
100 RuntimeBuilder {
101 globals: Some(values),
102 partials: self.partials,
103 }
104 }
105
106 pub fn set_partials<'n>(self, values: &'n dyn PartialStore) -> RuntimeBuilder<'g, 'n> {
108 RuntimeBuilder {
109 globals: self.globals,
110 partials: Some(values),
111 }
112 }
113
114 pub fn build(self) -> impl Runtime + 'c {
116 let partials = self.partials.unwrap_or(&NullPartials);
117 let runtime = RuntimeCore {
118 partials,
119 ..Default::default()
120 };
121 let runtime = super::IndexFrame::new(runtime);
122 let runtime = super::StackFrame::new(runtime, self.globals.unwrap_or(&NullObject));
123 super::GlobalFrame::new(runtime)
124 }
125}
126
127#[derive(Copy, Clone, Debug)]
128struct NullObject;
129
130impl ValueView for NullObject {
131 fn as_debug(&self) -> &dyn std::fmt::Debug {
132 self
133 }
134
135 fn render(&self) -> crate::model::DisplayCow<'_> {
136 Value::Nil.render()
137 }
138 fn source(&self) -> crate::model::DisplayCow<'_> {
139 Value::Nil.source()
140 }
141 fn type_name(&self) -> &'static str {
142 "object"
143 }
144 fn query_state(&self, state: crate::model::State) -> bool {
145 match state {
146 crate::model::State::Truthy => true,
147 crate::model::State::DefaultValue
148 | crate::model::State::Empty
149 | crate::model::State::Blank => false,
150 }
151 }
152
153 fn to_kstr(&self) -> crate::model::KStringCow<'_> {
154 crate::model::KStringCow::from_static("")
155 }
156 fn to_value(&self) -> Value {
157 Value::Object(Object::new())
158 }
159
160 fn as_object(&self) -> Option<&dyn ObjectView> {
161 Some(self)
162 }
163}
164
165impl ObjectView for NullObject {
166 fn as_value(&self) -> &dyn ValueView {
167 self
168 }
169
170 fn size(&self) -> i64 {
171 0
172 }
173
174 fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = crate::model::KStringCow<'k>> + 'k> {
175 let keys = Vec::new().into_iter();
176 Box::new(keys)
177 }
178
179 fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
180 let i = Vec::new().into_iter();
181 Box::new(i)
182 }
183
184 fn iter<'k>(
185 &'k self,
186 ) -> Box<dyn Iterator<Item = (crate::model::KStringCow<'k>, &'k dyn ValueView)> + 'k> {
187 let i = Vec::new().into_iter();
188 Box::new(i)
189 }
190
191 fn contains_key(&self, _index: &str) -> bool {
192 false
193 }
194
195 fn get<'s>(&'s self, _index: &str) -> Option<&'s dyn ValueView> {
196 None
197 }
198}
199
200impl Default for RuntimeBuilder<'static, 'static> {
201 fn default() -> Self {
202 Self::new()
203 }
204}
205
206pub struct RuntimeCore<'g> {
208 partials: &'g dyn PartialStore,
209
210 registers: Registers,
211}
212
213impl RuntimeCore<'_> {
214 pub fn new() -> Self {
218 RuntimeCore::default()
219 }
220
221 pub fn partials(&self) -> &dyn PartialStore {
223 self.partials
224 }
225}
226
227impl Runtime for RuntimeCore<'_> {
228 fn partials(&self) -> &dyn PartialStore {
229 self.partials
230 }
231
232 fn name(&self) -> Option<crate::model::KStringRef<'_>> {
233 None
234 }
235
236 fn roots(&self) -> std::collections::BTreeSet<crate::model::KStringCow<'_>> {
237 std::collections::BTreeSet::new()
239 }
240
241 fn try_get(&self, _path: &[ScalarCow<'_>]) -> Option<ValueCow<'_>> {
242 None
243 }
244
245 fn get(&self, path: &[ScalarCow<'_>]) -> Result<ValueCow<'_>> {
246 let key = path.first().cloned().unwrap_or_else(|| Scalar::new("nil"));
247 Error::with_msg("Unknown variable")
248 .context("requested variable", key.to_kstr())
249 .into_err()
250 }
251
252 fn set_global(
253 &self,
254 _name: crate::model::KString,
255 _val: crate::model::Value,
256 ) -> Option<crate::model::Value> {
257 unreachable!("Must be masked by a global frame");
258 }
259
260 fn set_index(&self, _name: crate::model::KString, _val: Value) -> Option<Value> {
261 unreachable!("Must be masked by a global frame");
262 }
263
264 fn get_index<'a>(&'a self, _name: &str) -> Option<ValueCow<'a>> {
265 None
266 }
267
268 fn registers(&self) -> &Registers {
269 &self.registers
270 }
271}
272
273impl Default for RuntimeCore<'_> {
274 fn default() -> Self {
275 Self {
276 partials: &NullPartials,
277 registers: Default::default(),
278 }
279 }
280}
281
282pub struct Registers {
284 registers: std::cell::RefCell<anymap2::AnyMap>,
285}
286
287impl Registers {
288 pub fn get_mut<T: std::any::Any + Default>(&self) -> std::cell::RefMut<'_, T> {
293 std::cell::RefMut::map(self.registers.borrow_mut(), |registers| {
294 registers.entry::<T>().or_default()
295 })
296 }
297}
298
299impl Default for Registers {
300 fn default() -> Self {
301 Self {
302 registers: std::cell::RefCell::new(anymap2::AnyMap::new()),
303 }
304 }
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, Default)]
313pub struct InterruptRegister {
314 interrupt: Option<Interrupt>,
315}
316
317impl InterruptRegister {
318 pub fn interrupted(&self) -> bool {
320 self.interrupt.is_some()
321 }
322
323 pub fn set(&mut self, interrupt: Interrupt) {
325 self.interrupt.replace(interrupt);
326 }
327
328 pub fn reset(&mut self) -> Option<Interrupt> {
330 self.interrupt.take()
331 }
332}
333
334#[derive(Debug, Clone, Copy, PartialEq, Eq)]
336pub enum Interrupt {
337 Continue,
339 Break,
341}
342
343#[derive(Copy, Clone, Debug)]
344struct NullPartials;
345
346impl PartialStore for NullPartials {
347 fn contains(&self, _name: &str) -> bool {
348 false
349 }
350
351 fn names(&self) -> Vec<&str> {
352 Vec::new()
353 }
354
355 fn try_get(&self, _name: &str) -> Option<sync::Arc<dyn Renderable>> {
356 None
357 }
358
359 fn get(&self, name: &str) -> Result<sync::Arc<dyn Renderable>> {
360 Err(Error::with_msg("Partial does not exist").context("name", name.to_owned()))
361 }
362}
363
364#[cfg(test)]
365mod test {
366 use super::*;
367
368 use crate::model::Scalar;
369 use crate::model::Value;
370 use crate::model::ValueViewCmp;
371
372 #[test]
373 fn mask_variables() {
374 let test_path = [Scalar::new("test")];
375
376 let rt = RuntimeBuilder::new().build();
377 rt.set_global("test".into(), Value::scalar(42f64));
378 assert_eq!(&rt.get(&test_path).unwrap(), &ValueViewCmp::new(&42f64));
379
380 {
381 let data = crate::object!({"test": 3});
382 let new_scope = super::super::StackFrame::new(&rt, &data);
383
384 assert_eq!(&new_scope.get(&test_path).unwrap(), &ValueViewCmp::new(&3));
386 }
387
388 assert_eq!(&rt.get(&test_path).unwrap(), &ValueViewCmp::new(&42));
390 }
391
392 #[test]
393 fn global_variables() {
394 let global_path = [Scalar::new("global")];
395
396 let rt = RuntimeBuilder::new().build();
397
398 {
399 let data = crate::object!({"test": 3});
400 let new_scope = super::super::StackFrame::new(&rt, &data);
401
402 new_scope.set_global("global".into(), Value::scalar("some value"));
404 }
405 assert_eq!(
406 &rt.get(&global_path).unwrap(),
407 &ValueViewCmp::new(&"some value")
408 );
409 }
410}