loose_liquid_core/runtime/
runtime.rs1use 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, R: Runtime + ?Sized> Runtime for &'r 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<'g> RuntimeCore<'g> {
214 pub fn new() -> Self {
218 RuntimeCore::default()
219 }
220
221 pub fn partials(&self) -> &dyn PartialStore {
223 self.partials
224 }
225}
226
227impl<'g> Runtime for RuntimeCore<'g> {
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::unknown_variable(key.to_kstr()).into_err()
248 }
249
250 fn set_global(
251 &self,
252 _name: crate::model::KString,
253 _val: crate::model::Value,
254 ) -> Option<crate::model::Value> {
255 unreachable!("Must be masked by a global frame");
256 }
257
258 fn set_index(&self, _name: crate::model::KString, _val: Value) -> Option<Value> {
259 unreachable!("Must be masked by a global frame");
260 }
261
262 fn get_index<'a>(&'a self, _name: &str) -> Option<ValueCow<'a>> {
263 None
264 }
265
266 fn registers(&self) -> &Registers {
267 &self.registers
268 }
269}
270
271impl<'g> Default for RuntimeCore<'g> {
272 fn default() -> Self {
273 Self {
274 partials: &NullPartials,
275 registers: Default::default(),
276 }
277 }
278}
279
280pub struct Registers {
282 registers: std::cell::RefCell<anymap2::AnyMap>,
283}
284
285impl Registers {
286 pub fn get_mut<T: std::any::Any + Default>(&self) -> std::cell::RefMut<'_, T> {
291 std::cell::RefMut::map(self.registers.borrow_mut(), |registers| {
292 registers.entry::<T>().or_insert_with(Default::default)
293 })
294 }
295}
296
297impl Default for Registers {
298 fn default() -> Self {
299 Self {
300 registers: std::cell::RefCell::new(anymap2::AnyMap::new()),
301 }
302 }
303}
304
305#[derive(Debug, Clone, PartialEq, Eq, Default)]
311pub struct InterruptRegister {
312 interrupt: Option<Interrupt>,
313}
314
315impl InterruptRegister {
316 pub fn interrupted(&self) -> bool {
318 self.interrupt.is_some()
319 }
320
321 pub fn set(&mut self, interrupt: Interrupt) {
323 self.interrupt.replace(interrupt);
324 }
325
326 pub fn reset(&mut self) -> Option<Interrupt> {
328 self.interrupt.take()
329 }
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334pub enum Interrupt {
335 Continue,
337 Break,
339}
340
341#[derive(Copy, Clone, Debug)]
342struct NullPartials;
343
344impl PartialStore for NullPartials {
345 fn contains(&self, _name: &str) -> bool {
346 false
347 }
348
349 fn names(&self) -> Vec<&str> {
350 Vec::new()
351 }
352
353 fn try_get(&self, _name: &str) -> Option<sync::Arc<dyn Renderable>> {
354 None
355 }
356
357 fn get(&self, name: &str) -> Result<sync::Arc<dyn Renderable>> {
358 Err(Error::with_msg("Partial does not exist").context("name", name.to_owned()))
359 }
360}
361
362#[cfg(test)]
363mod test {
364 use super::*;
365
366 use crate::model::Scalar;
367 use crate::model::Value;
368 use crate::model::ValueViewCmp;
369
370 #[test]
371 fn mask_variables() {
372 let test_path = [Scalar::new("test")];
373
374 let rt = RuntimeBuilder::new().build();
375 rt.set_global("test".into(), Value::scalar(42f64));
376 assert_eq!(&rt.get(&test_path).unwrap(), &ValueViewCmp::new(&42f64));
377
378 {
379 let data = crate::object!({"test": 3});
380 let new_scope = super::super::StackFrame::new(&rt, &data);
381
382 assert_eq!(&new_scope.get(&test_path).unwrap(), &ValueViewCmp::new(&3));
384 }
385
386 assert_eq!(&rt.get(&test_path).unwrap(), &ValueViewCmp::new(&42));
388 }
389
390 #[test]
391 fn global_variables() {
392 let global_path = [Scalar::new("global")];
393
394 let rt = RuntimeBuilder::new().build();
395
396 {
397 let data = crate::object!({"test": 3});
398 let new_scope = super::super::StackFrame::new(&rt, &data);
399
400 new_scope.set_global("global".into(), Value::scalar("some value"));
402 }
403 assert_eq!(
404 &rt.get(&global_path).unwrap(),
405 &ValueViewCmp::new(&"some value")
406 );
407 }
408}