tera/renderer/
call_stack.rs1use std::borrow::Cow;
2use std::collections::HashMap;
3
4use serde_json::{to_value, Value};
5
6use crate::context::dotted_pointer;
7use crate::errors::{Error, Result};
8use crate::renderer::for_loop::{ForLoop, ForLoopState};
9use crate::renderer::stack_frame::{FrameContext, FrameType, StackFrame, Val};
10use crate::template::Template;
11use crate::Context;
12
13#[derive(Debug)]
15pub struct UserContext<'a> {
16 inner: &'a Context,
18}
19
20impl<'a> UserContext<'a> {
21 pub fn new(context: &'a Context) -> Self {
23 UserContext { inner: context }
24 }
25
26 pub fn find_value(&self, key: &str) -> Option<&'a Value> {
27 self.inner.get(key)
28 }
29
30 pub fn find_value_by_dotted_pointer(&self, pointer: &str) -> Option<&'a Value> {
31 let root = pointer.split('.').next().unwrap().replace("~1", "/").replace("~0", "~");
32 let rest = &pointer[root.len() + 1..];
33 self.inner.get(&root).and_then(|val| dotted_pointer(val, rest))
34 }
35}
36
37#[derive(Debug)]
39pub struct CallStack<'a> {
40 stack: Vec<StackFrame<'a>>,
42 context: UserContext<'a>,
44}
45
46impl<'a> CallStack<'a> {
47 pub fn new(context: &'a Context, template: &'a Template) -> CallStack<'a> {
49 CallStack {
50 stack: vec![StackFrame::new(FrameType::Origin, "ORIGIN", template)],
51 context: UserContext::new(context),
52 }
53 }
54
55 pub fn push_for_loop_frame(&mut self, name: &'a str, for_loop: ForLoop<'a>) {
56 let tpl = self.stack.last().expect("Stack frame").active_template;
57 self.stack.push(StackFrame::new_for_loop(name, tpl, for_loop));
58 }
59
60 pub fn push_macro_frame(
61 &mut self,
62 namespace: &'a str,
63 name: &'a str,
64 context: FrameContext<'a>,
65 tpl: &'a Template,
66 ) {
67 self.stack.push(StackFrame::new_macro(name, tpl, namespace, context));
68 }
69
70 pub fn push_include_frame(&mut self, name: &'a str, tpl: &'a Template) {
71 self.stack.push(StackFrame::new_include(name, tpl));
72 }
73
74 pub fn global_frame_mut(&mut self) -> &mut StackFrame<'a> {
77 if self.current_frame().kind == FrameType::ForLoop {
78 for stack_frame in self.stack.iter_mut().rev() {
79 if stack_frame.kind != FrameType::ForLoop {
81 return stack_frame;
82 }
83 }
84 unreachable!("Global frame not found when trying to break out of for loop");
85 } else {
86 self.current_frame_mut()
88 }
89 }
90
91 pub fn current_frame_mut(&mut self) -> &mut StackFrame<'a> {
93 self.stack.last_mut().expect("No current frame exists")
94 }
95
96 pub fn current_frame(&self) -> &StackFrame<'a> {
98 self.stack.last().expect("No current frame exists")
99 }
100
101 pub fn pop(&mut self) {
103 self.stack.pop().expect("Mistakenly popped Origin frame");
104 }
105
106 pub fn lookup(&self, key: &str) -> Option<Val<'a>> {
107 for stack_frame in self.stack.iter().rev() {
108 let found = stack_frame.find_value(key);
109 if found.is_some() {
110 return found;
111 }
112
113 if stack_frame.kind == FrameType::Macro || stack_frame.kind == FrameType::Origin {
116 break;
117 }
118 }
119
120 if key.contains('.') {
122 return self.context.find_value_by_dotted_pointer(key).map(Cow::Borrowed);
123 } else if let Some(value) = self.context.find_value(key) {
124 return Some(Cow::Borrowed(value));
125 }
126
127 None
128 }
129
130 pub fn add_assignment(&mut self, key: &'a str, global: bool, value: Val<'a>) {
132 if global {
133 self.global_frame_mut().insert(key, value);
134 } else {
135 self.current_frame_mut().insert(key, value);
136 }
137 }
138
139 pub fn break_for_loop(&mut self) -> Result<()> {
141 match self.current_frame_mut().for_loop {
142 Some(ref mut for_loop) => {
143 for_loop.break_loop();
144 Ok(())
145 }
146 None => Err(Error::msg("Attempted `break` while not in `for loop`")),
147 }
148 }
149
150 pub fn increment_for_loop(&mut self) -> Result<()> {
152 let frame = self.current_frame_mut();
153 frame.clear_context();
154 match frame.for_loop {
155 Some(ref mut for_loop) => {
156 for_loop.increment();
157 Ok(())
158 }
159 None => Err(Error::msg("Attempted `increment` while not in `for loop`")),
160 }
161 }
162
163 pub fn continue_for_loop(&mut self) -> Result<()> {
165 match self.current_frame_mut().for_loop {
166 Some(ref mut for_loop) => {
167 for_loop.continue_loop();
168 Ok(())
169 }
170 None => Err(Error::msg("Attempted `continue` while not in `for loop`")),
171 }
172 }
173
174 pub fn should_break_body(&self) -> bool {
176 match self.current_frame().for_loop {
177 Some(ref for_loop) => {
178 for_loop.state == ForLoopState::Break || for_loop.state == ForLoopState::Continue
179 }
180 None => false,
181 }
182 }
183
184 pub fn should_break_for_loop(&self) -> bool {
186 match self.current_frame().for_loop {
187 Some(ref for_loop) => for_loop.state == ForLoopState::Break,
188 None => false,
189 }
190 }
191
192 pub fn active_template(&self) -> &'a Template {
194 self.current_frame().active_template
195 }
196
197 pub fn current_context_cloned(&self) -> Value {
198 let mut context = HashMap::new();
199
200 for frame in self.stack.iter().rev() {
202 context.extend(frame.context_owned());
203 if let Some(ref for_loop) = frame.for_loop {
204 context.insert(
205 for_loop.value_name.to_string(),
206 for_loop.get_current_value().into_owned(),
207 );
208 if for_loop.is_key_value() {
209 context.insert(
210 for_loop.key_name.clone().unwrap(),
211 Value::String(for_loop.get_current_key()),
212 );
213 }
214 }
215 if frame.kind == FrameType::Macro {
217 return to_value(&context).unwrap();
218 }
219 }
220
221 let mut new_ctx = self.context.inner.clone();
225 for (key, val) in context {
226 new_ctx.insert(key, &val)
227 }
228 new_ctx.into_json()
229 }
230}