liquid_interpreter/
context.rs1use std::sync;
2
3use anymap;
4use liquid_error::Error;
5use liquid_error::Result;
6
7use super::PartialStore;
8use super::Renderable;
9use super::Stack;
10use super::ValueStore;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Interrupt {
15 Continue,
17 Break,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Default)]
27pub struct InterruptState {
28 interrupt: Option<Interrupt>,
29}
30
31impl InterruptState {
32 pub fn interrupted(&self) -> bool {
34 self.interrupt.is_some()
35 }
36
37 pub fn set_interrupt(&mut self, interrupt: Interrupt) {
39 self.interrupt = Some(interrupt);
40 }
41
42 pub fn pop_interrupt(&mut self) -> Option<Interrupt> {
44 let rval = self.interrupt;
45 self.interrupt = None;
46 rval
47 }
48}
49
50#[derive(Copy, Clone, Debug)]
51struct NullPartials;
52
53impl PartialStore for NullPartials {
54 fn contains(&self, _name: &str) -> bool {
55 false
56 }
57
58 fn names(&self) -> Vec<&str> {
59 Vec::new()
60 }
61
62 fn try_get(&self, _name: &str) -> Option<sync::Arc<Renderable>> {
63 None
64 }
65
66 fn get(&self, name: &str) -> Result<sync::Arc<Renderable>> {
67 Err(Error::with_msg("Partial does not exist").context("name", name.to_owned()))
68 }
69}
70
71pub struct ContextBuilder<'g> {
73 globals: Option<&'g ValueStore>,
74 partials: Option<&'g PartialStore>,
75}
76
77impl<'g> ContextBuilder<'g> {
78 pub fn new() -> Self {
80 Self {
81 globals: None,
82 partials: None,
83 }
84 }
85
86 pub fn set_globals(mut self, values: &'g ValueStore) -> Self {
88 self.globals = Some(values);
89 self
90 }
91
92 pub fn set_partials(mut self, values: &'g PartialStore) -> Self {
94 self.partials = Some(values);
95 self
96 }
97
98 pub fn build(self) -> Context<'g> {
100 let stack = match self.globals {
101 Some(globals) => Stack::with_globals(globals),
102 None => Stack::empty(),
103 };
104 let partials = self.partials.unwrap_or(&NullPartials);
105 Context {
106 stack,
107 partials,
108 registers: anymap::AnyMap::new(),
109 interrupt: InterruptState::default(),
110 }
111 }
112}
113
114impl<'g> Default for ContextBuilder<'g> {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120pub struct Context<'g> {
122 stack: Stack<'g>,
123 partials: &'g PartialStore,
124
125 registers: anymap::AnyMap,
126 interrupt: InterruptState,
127}
128
129impl<'g> Context<'g> {
130 pub fn new() -> Self {
134 Context::default()
135 }
136
137 pub fn interrupt(&self) -> &InterruptState {
139 &self.interrupt
140 }
141
142 pub fn interrupt_mut(&mut self) -> &mut InterruptState {
144 &mut self.interrupt
145 }
146
147 pub fn partials(&self) -> &PartialStore {
149 self.partials
150 }
151
152 pub fn get_register_mut<T: anymap::any::IntoBox<anymap::any::Any> + Default>(
157 &mut self,
158 ) -> &mut T {
159 self.registers.entry::<T>().or_insert_with(Default::default)
160 }
161
162 pub fn stack(&self) -> &Stack {
164 &self.stack
165 }
166
167 pub fn stack_mut<'a>(&'a mut self) -> &'a mut Stack<'g>
169 where
170 'g: 'a,
171 {
172 &mut self.stack
173 }
174
175 pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
179 where
180 FnT: FnOnce(&mut Context) -> RvalT,
181 {
182 self.stack.push_frame();
183 let result = f(self);
184 self.stack.pop_frame();
185 result
186 }
187
188 pub fn run_in_named_scope<RvalT, S: Into<String>, FnT>(&mut self, name: S, f: FnT) -> RvalT
192 where
193 FnT: FnOnce(&mut Context) -> RvalT,
194 {
195 self.stack.push_named_frame(name);
196 let result = f(self);
197 self.stack.pop_frame();
198 result
199 }
200}
201
202impl<'g> Default for Context<'g> {
203 fn default() -> Self {
204 Self {
205 stack: Stack::empty(),
206 partials: &NullPartials,
207 registers: anymap::AnyMap::new(),
208 interrupt: InterruptState::default(),
209 }
210 }
211}
212
213#[cfg(test)]
214mod test {
215 use super::*;
216
217 use liquid_value::Scalar;
218 use liquid_value::Value;
219
220 #[test]
221 fn scoped_variables() {
222 let test_path = [Scalar::new("test")];
223 let global_path = [Scalar::new("global")];
224
225 let mut ctx = Context::new();
226 ctx.stack_mut().set_global("test", Value::scalar(42f64));
227 assert_eq!(ctx.stack().get(&test_path).unwrap(), &Value::scalar(42f64));
228
229 ctx.run_in_scope(|new_scope| {
230 assert_eq!(
232 new_scope.stack().get(&test_path).unwrap(),
233 &Value::scalar(42f64)
234 );
235
236 new_scope.stack_mut().set("test", Value::scalar(3.14f64));
238 assert_eq!(
239 new_scope.stack().get(&test_path).unwrap(),
240 &Value::scalar(3.14f64)
241 );
242
243 new_scope
245 .stack_mut()
246 .set_global("global", Value::scalar("some value"));
247 });
248
249 assert_eq!(ctx.stack().get(&test_path).unwrap(), &Value::scalar(42f64));
251 assert_eq!(
252 ctx.stack().get(&global_path).unwrap(),
253 &Value::scalar("some value")
254 );
255 }
256}