liquid_interpreter/
stack.rs1use std::borrow;
2
3use itertools;
4use liquid_error::{Error, Result};
5use liquid_value::{Object, PathRef, Scalar, Value};
6
7use super::ValueStore;
8
9#[derive(Clone, Default, Debug)]
10struct Frame {
11 name: Option<String>,
12 data: Object,
13}
14
15impl Frame {
16 fn new() -> Self {
17 Default::default()
18 }
19
20 fn with_name<S: Into<String>>(name: S) -> Self {
21 Self {
22 name: Some(name.into()),
23 data: Object::new(),
24 }
25 }
26}
27
28#[derive(Debug, Clone)]
30pub struct Stack<'g> {
31 globals: Option<&'g ValueStore>,
32 stack: Vec<Frame>,
33 indexes: Object,
35}
36
37impl<'g> Stack<'g> {
38 pub fn empty() -> Self {
40 Self {
41 globals: None,
42 indexes: Object::new(),
43 stack: vec![Frame::new()],
45 }
46 }
47
48 pub fn with_globals(globals: &'g ValueStore) -> Self {
50 let mut stack = Self::empty();
51 stack.globals = Some(globals);
52 stack
53 }
54
55 pub(crate) fn push_frame(&mut self) {
57 self.stack.push(Frame::new());
58 }
59
60 pub(crate) fn push_named_frame<S: Into<String>>(&mut self, name: S) {
62 self.stack.push(Frame::with_name(name));
63 }
64
65 pub(crate) fn pop_frame(&mut self) {
74 if self.stack.pop().is_none() {
75 panic!("Unbalanced push/pop, leaving the stack empty.")
76 };
77 }
78
79 pub fn frame_name(&self) -> Option<&str> {
81 self.stack
82 .iter()
83 .rev()
84 .find_map(|f| f.name.as_ref().map(|s| s.as_str()))
85 }
86
87 pub fn try_get(&self, path: PathRef) -> Option<&Value> {
89 let frame = self.find_path_frame(path)?;
90
91 frame.try_get_variable(path)
92 }
93
94 pub fn get(&self, path: PathRef) -> Result<&Value> {
96 let frame = self.find_path_frame(path).ok_or_else(|| {
97 let key = path
98 .iter()
99 .next()
100 .cloned()
101 .unwrap_or_else(|| Scalar::new("nil"));
102 let globals = itertools::join(self.globals().iter(), ", ");
103 Error::with_msg("Unknown variable")
104 .context("requested variable", key.to_str().into_owned())
105 .context("available variables", globals)
106 })?;
107
108 frame.get_variable(path)
109 }
110
111 fn globals(&self) -> Vec<&str> {
112 let mut globals = self.globals.map(|g| g.roots()).unwrap_or_default();
113 for frame in self.stack.iter() {
114 globals.extend(frame.data.roots());
115 }
116 globals.sort();
117 globals.dedup();
118 globals
119 }
120
121 fn find_path_frame<'a>(&'a self, path: PathRef) -> Option<&'a ValueStore> {
122 let key = path.iter().next()?;
123 let key = key.to_str();
124 self.find_frame(key.as_ref())
125 }
126
127 fn find_frame<'a>(&'a self, name: &str) -> Option<&'a ValueStore> {
128 for frame in self.stack.iter().rev() {
129 if frame.data.contains_root(name) {
130 return Some(&frame.data);
131 }
132 }
133
134 if self.globals.map(|g| g.contains_root(name)).unwrap_or(false) {
135 return self.globals;
136 }
137
138 if self.indexes.contains_root(name) {
139 return Some(&self.indexes);
140 }
141
142 None
143 }
144
145 pub fn set_index<S>(&mut self, name: S, val: Value) -> Option<Value>
147 where
148 S: Into<borrow::Cow<'static, str>>,
149 {
150 self.indexes.insert(name.into(), val)
151 }
152
153 pub fn get_index<'a>(&'a self, name: &str) -> Option<&'a Value> {
155 self.indexes.get(name)
156 }
157
158 pub fn set_global<S>(&mut self, name: S, val: Value) -> Option<Value>
160 where
161 S: Into<borrow::Cow<'static, str>>,
162 {
163 self.global_frame().insert(name.into(), val)
164 }
165
166 pub fn set<S>(&mut self, name: S, val: Value) -> Option<Value>
175 where
176 S: Into<borrow::Cow<'static, str>>,
177 {
178 self.current_frame().insert(name.into(), val)
179 }
180
181 fn current_frame(&mut self) -> &mut Object {
182 match self.stack.last_mut() {
183 Some(frame) => &mut frame.data,
184 None => panic!("Global frame removed."),
185 }
186 }
187
188 fn global_frame(&mut self) -> &mut Object {
189 match self.stack.first_mut() {
190 Some(frame) => &mut frame.data,
191 None => panic!("Global frame removed."),
192 }
193 }
194}
195
196impl<'g> Default for Stack<'g> {
197 fn default() -> Self {
198 Self::empty()
199 }
200}
201
202#[cfg(test)]
203mod test {
204 use super::*;
205
206 #[test]
207 fn stack_find_frame() {
208 let mut stack = Stack::empty();
209 stack.set_global("number", Value::scalar(42f64));
210 assert!(stack.find_frame("number").is_some(),);
211 }
212
213 #[test]
214 fn stack_find_frame_failure() {
215 let mut stack = Stack::empty();
216 let mut post = Object::new();
217 post.insert("number".into(), Value::scalar(42f64));
218 stack.set_global("post", Value::Object(post));
219 assert!(stack.find_frame("post.number").is_none());
220 }
221
222 #[test]
223 fn stack_get() {
224 let mut stack = Stack::empty();
225 let mut post = Object::new();
226 post.insert("number".into(), Value::scalar(42f64));
227 stack.set_global("post", Value::Object(post));
228 let indexes = [Scalar::new("post"), Scalar::new("number")];
229 assert_eq!(stack.get(&indexes).unwrap(), &Value::scalar(42f64));
230 }
231
232}