loose_liquid_core/runtime/
stack.rs1use crate::error::Error;
2use crate::error::Result;
3use crate::model::{Object, ObjectView, ScalarCow, Value, ValueCow, ValueView};
4
5use super::Registers;
6
7pub struct StackFrame<P, O> {
9 parent: P,
10 name: Option<crate::model::KString>,
11 data: O,
12}
13
14impl<P: super::Runtime, O: ObjectView> StackFrame<P, O> {
15 pub fn new(parent: P, data: O) -> Self {
17 Self {
18 parent,
19 name: None,
20 data,
21 }
22 }
23
24 pub fn with_name<S: Into<crate::model::KString>>(mut self, name: S) -> Self {
26 self.name = Some(name.into());
27 self
28 }
29}
30
31impl<P: super::Runtime, O: ObjectView> super::Runtime for StackFrame<P, O> {
32 fn partials(&self) -> &dyn super::PartialStore {
33 self.parent.partials()
34 }
35
36 fn name(&self) -> Option<crate::model::KStringRef<'_>> {
37 self.name
38 .as_ref()
39 .map(|n| n.as_ref())
40 .or_else(|| self.parent.name())
41 }
42
43 fn roots(&self) -> std::collections::BTreeSet<crate::model::KStringCow<'_>> {
44 let mut roots = self.parent.roots();
45 roots.extend(self.data.keys());
46 roots
47 }
48
49 fn try_get(&self, path: &[ScalarCow<'_>]) -> Option<ValueCow<'_>> {
50 let key = path.first()?;
51 let key = key.to_kstr();
52 let data = &self.data;
53 if data.contains_key(key.as_str()) {
54 crate::model::try_find(data.as_value(), path)
55 } else {
56 self.parent.try_get(path)
57 }
58 }
59
60 fn get(&self, path: &[ScalarCow<'_>]) -> Result<ValueCow<'_>> {
61 let key = path.first().ok_or_else(|| Error::unknown_variable("nil"))?;
62 let key = key.to_kstr();
63 let data = &self.data;
64 if data.contains_key(key.as_str()) {
65 crate::model::find(data.as_value(), path).map(|v| v.into_owned().into())
66 } else {
67 self.parent.get(path)
68 }
69 }
70
71 fn set_global(
72 &self,
73 name: crate::model::KString,
74 val: crate::model::Value,
75 ) -> Option<crate::model::Value> {
76 self.parent.set_global(name, val)
77 }
78
79 fn set_index(&self, name: crate::model::KString, val: Value) -> Option<Value> {
80 self.parent.set_index(name, val)
81 }
82
83 fn get_index<'a>(&'a self, name: &str) -> Option<ValueCow<'a>> {
84 self.parent.get_index(name)
85 }
86
87 fn registers(&self) -> &super::Registers {
88 self.parent.registers()
89 }
90}
91
92pub struct GlobalFrame<P> {
94 parent: P,
95 data: std::cell::RefCell<Object>,
96}
97
98impl<P: super::Runtime> GlobalFrame<P> {
99 pub fn new(parent: P) -> Self {
101 Self {
102 parent,
103 data: Default::default(),
104 }
105 }
106}
107
108impl<P: super::Runtime> super::Runtime for GlobalFrame<P> {
109 fn partials(&self) -> &dyn super::PartialStore {
110 self.parent.partials()
111 }
112
113 fn name(&self) -> Option<crate::model::KStringRef<'_>> {
114 self.parent.name()
115 }
116
117 fn roots(&self) -> std::collections::BTreeSet<crate::model::KStringCow<'_>> {
118 let mut roots = self.parent.roots();
119 roots.extend(self.data.borrow().keys().map(|k| k.clone().into()));
120 roots
121 }
122
123 fn try_get(&self, path: &[ScalarCow<'_>]) -> Option<ValueCow<'_>> {
124 let key = path.first()?;
125 let key = key.to_kstr();
126 let data = self.data.borrow();
127 if data.contains_key(key.as_str()) {
128 crate::model::try_find(data.as_value(), path).map(|v| v.into_owned().into())
129 } else {
130 self.parent.try_get(path)
131 }
132 }
133
134 fn get(&self, path: &[ScalarCow<'_>]) -> Result<ValueCow<'_>> {
135 let key = path.first().ok_or_else(|| Error::unknown_variable("nil"))?;
136 let key = key.to_kstr();
137 let data = self.data.borrow();
138 if data.contains_key(key.as_str()) {
139 crate::model::find(data.as_value(), path).map(|v| v.into_owned().into())
140 } else {
141 self.parent.get(path)
142 }
143 }
144
145 fn set_global(
146 &self,
147 name: crate::model::KString,
148 val: crate::model::Value,
149 ) -> Option<crate::model::Value> {
150 let mut data = self.data.borrow_mut();
151 data.insert(name, val)
152 }
153
154 fn set_index(&self, name: crate::model::KString, val: Value) -> Option<Value> {
155 self.parent.set_index(name, val)
156 }
157
158 fn get_index<'a>(&'a self, name: &str) -> Option<ValueCow<'a>> {
159 self.parent.get_index(name)
160 }
161
162 fn registers(&self) -> &super::Registers {
163 self.parent.registers()
164 }
165}
166
167pub(crate) struct IndexFrame<P> {
168 parent: P,
169 data: std::cell::RefCell<Object>,
170}
171
172impl<P: super::Runtime> IndexFrame<P> {
173 pub fn new(parent: P) -> Self {
174 Self {
175 parent,
176 data: Default::default(),
177 }
178 }
179}
180
181impl<P: super::Runtime> super::Runtime for IndexFrame<P> {
182 fn partials(&self) -> &dyn super::PartialStore {
183 self.parent.partials()
184 }
185
186 fn name(&self) -> Option<crate::model::KStringRef<'_>> {
187 self.parent.name()
188 }
189
190 fn roots(&self) -> std::collections::BTreeSet<crate::model::KStringCow<'_>> {
191 let mut roots = self.parent.roots();
192 roots.extend(self.data.borrow().keys().map(|k| k.clone().into()));
193 roots
194 }
195
196 fn try_get(&self, path: &[ScalarCow<'_>]) -> Option<ValueCow<'_>> {
197 let key = path.first()?;
198 let key = key.to_kstr();
199 let data = self.data.borrow();
200 if data.contains_key(key.as_str()) {
201 crate::model::try_find(data.as_value(), path).map(|v| v.into_owned().into())
202 } else {
203 self.parent.try_get(path)
204 }
205 }
206
207 fn get(&self, path: &[ScalarCow<'_>]) -> Result<ValueCow<'_>> {
208 let key = path.first().ok_or_else(|| Error::unknown_variable("nil"))?;
209 let key = key.to_kstr();
210 let data = self.data.borrow();
211 if data.contains_key(key.as_str()) {
212 crate::model::find(data.as_value(), path).map(|v| v.into_owned().into())
213 } else {
214 self.parent.get(path)
215 }
216 }
217
218 fn set_global(
219 &self,
220 name: crate::model::KString,
221 val: crate::model::Value,
222 ) -> Option<crate::model::Value> {
223 self.parent.set_global(name, val)
224 }
225
226 fn set_index(&self, name: crate::model::KString, val: Value) -> Option<Value> {
227 let mut data = self.data.borrow_mut();
228 data.insert(name, val)
229 }
230
231 fn get_index<'a>(&'a self, name: &str) -> Option<ValueCow<'a>> {
232 self.data.borrow().get(name).map(|v| v.to_value().into())
233 }
234
235 fn registers(&self) -> &super::Registers {
236 self.parent.registers()
237 }
238}
239
240pub struct SandboxedStackFrame<P, O> {
243 parent: P,
244 name: Option<crate::model::KString>,
245 data: O,
246 registers: Registers,
247}
248
249impl<P: super::Runtime, O: ObjectView> SandboxedStackFrame<P, O> {
250 pub fn new(parent: P, data: O) -> Self {
252 Self {
253 parent,
254 name: None,
255 data,
256 registers: Default::default(),
257 }
258 }
259
260 pub fn with_name<S: Into<crate::model::KString>>(mut self, name: S) -> Self {
262 self.name = Some(name.into());
263 self
264 }
265}
266
267impl<P: super::Runtime, O: ObjectView> super::Runtime for SandboxedStackFrame<P, O> {
268 fn partials(&self) -> &dyn super::PartialStore {
269 self.parent.partials()
270 }
271
272 fn name(&self) -> Option<crate::model::KStringRef<'_>> {
273 self.name
274 .as_ref()
275 .map(|n| n.as_ref())
276 .or_else(|| self.parent.name())
277 }
278
279 fn roots(&self) -> std::collections::BTreeSet<crate::model::KStringCow<'_>> {
280 let mut roots = std::collections::BTreeSet::new();
281 roots.extend(self.data.keys());
282 roots
283 }
284
285 fn try_get(&self, path: &[ScalarCow<'_>]) -> Option<ValueCow<'_>> {
286 let key = path.first()?;
287 let key = key.to_kstr();
288 let data = &self.data;
289 data.get(key.as_str())
290 .and_then(|_| crate::model::try_find(data.as_value(), path))
291 }
292
293 fn get(&self, path: &[ScalarCow<'_>]) -> Result<ValueCow<'_>> {
294 let key = path.first().ok_or_else(|| {
295 Error::with_msg("Unknown variable").context("requested variable", "nil")
296 })?;
297 let key = key.to_kstr();
298 let data = &self.data;
299 data.get(key.as_str())
300 .and_then(|_| crate::model::try_find(data.as_value(), path))
301 .map(|v| v.into_owned().into())
302 .ok_or_else(|| Error::with_msg("Unknown variable").context("requested variable", key))
303 }
304
305 fn set_global(
306 &self,
307 name: crate::model::KString,
308 val: crate::model::Value,
309 ) -> Option<crate::model::Value> {
310 self.parent.set_global(name, val)
311 }
312
313 fn set_index(&self, name: crate::model::KString, val: Value) -> Option<Value> {
314 self.parent.set_index(name, val)
315 }
316
317 fn get_index<'a>(&'a self, name: &str) -> Option<ValueCow<'a>> {
318 self.parent.get_index(name)
319 }
320
321 fn registers(&self) -> &super::Registers {
322 &self.registers
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use crate::{runtime::RuntimeBuilder, Runtime};
329
330 use super::*;
331
332 #[test]
333 fn test_opaque_stack_frame_try_get() {
334 let globals = {
335 let mut o = Object::new();
336 o.insert("a".into(), Value::Scalar(1i64.into()));
337 o
338 };
339 let runtime = RuntimeBuilder::new().set_globals(&globals).build();
340 let opaque_stack_frame = SandboxedStackFrame::new(&runtime, {
341 let mut o = Object::new();
342 o.insert("b".into(), Value::Scalar(2i64.into()));
343 o
344 });
345
346 assert!(opaque_stack_frame.try_get(&["a".into()]).is_none());
348 assert!(opaque_stack_frame.try_get(&["b".into()]).is_some());
349
350 let stack_frame = StackFrame::new(opaque_stack_frame, {
351 let mut o = Object::new();
352 o.insert("c".into(), Value::Scalar(1i64.into()));
353 o
354 });
355
356 assert!(stack_frame.try_get(&["a".into()]).is_none());
358 assert!(stack_frame.try_get(&["b".into()]).is_some());
359 assert!(stack_frame.try_get(&["c".into()]).is_some());
360 }
361
362 #[test]
363 fn test_opaque_stack_frame_get() {
364 let globals = {
365 let mut o = Object::new();
366 o.insert("a".into(), Value::Scalar(1i64.into()));
367 o
368 };
369 let runtime = RuntimeBuilder::new().set_globals(&globals).build();
370 let opaque_stack_frame = SandboxedStackFrame::new(&runtime, {
371 let mut o = Object::new();
372 o.insert("b".into(), Value::Scalar(2i64.into()));
373 o
374 });
375
376 assert!(opaque_stack_frame.get(&["a".into()]).is_err());
378 assert!(opaque_stack_frame.get(&["b".into()]).is_ok());
379
380 let stack_frame = StackFrame::new(opaque_stack_frame, {
381 let mut o = Object::new();
382 o.insert("c".into(), Value::Scalar(1i64.into()));
383 o
384 });
385
386 assert!(stack_frame.get(&["a".into()]).is_err());
388 assert!(stack_frame.get(&["b".into()]).is_ok());
389 assert!(stack_frame.get(&["c".into()]).is_ok());
390 }
391
392 #[test]
393 fn test_opaque_stack_frame_roots() {
394 let globals = {
395 let mut o = Object::new();
396 o.insert("a".into(), Value::Scalar(1i64.into()));
397 o
398 };
399 let runtime = RuntimeBuilder::new().set_globals(&globals).build();
400 let opaque_stack_frame = SandboxedStackFrame::new(&runtime, {
401 let mut o = Object::new();
402 o.insert("b".into(), Value::Scalar(2i64.into()));
403 o
404 });
405 let roots = opaque_stack_frame.roots();
406
407 assert!(!roots.contains("a"));
409 assert!(roots.contains("b"));
410 }
411}