]> git.proxmox.com Git - rustc.git/blob - vendor/tera/src/renderer/stack_frame.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / vendor / tera / src / renderer / stack_frame.rs
1 use std::borrow::Cow;
2 use std::collections::HashMap;
3
4 use serde_json::Value;
5
6 use crate::context::get_json_pointer;
7 use crate::renderer::for_loop::ForLoop;
8 use crate::template::Template;
9
10 pub type Val<'a> = Cow<'a, Value>;
11 pub type FrameContext<'a> = HashMap<&'a str, Val<'a>>;
12
13 /// Gets a value within a value by pointer, keeping lifetime
14 #[inline]
15 pub fn value_by_pointer<'a>(pointer: &str, val: &Val<'a>) -> Option<Val<'a>> {
16 match *val {
17 Cow::Borrowed(r) => r.pointer(&get_json_pointer(pointer)).map(|found| Cow::Borrowed(found)),
18 Cow::Owned(ref r) => {
19 r.pointer(&get_json_pointer(pointer)).map(|found| Cow::Owned(found.clone()))
20 }
21 }
22 }
23
24 /// Enumerates the types of stack frames
25 #[derive(Clone, Copy, Debug, PartialEq)]
26 pub enum FrameType {
27 /// Original frame
28 Origin,
29 /// New frame for macro call
30 Macro,
31 /// New frame for for loop
32 ForLoop,
33 /// Include template
34 Include,
35 }
36
37 /// Entry in the stack frame
38 #[derive(Debug)]
39 pub struct StackFrame<'a> {
40 /// Type of stack frame
41 pub kind: FrameType,
42 /// Frame name for context/debugging
43 pub name: &'a str,
44 /// Assigned value (via {% set ... %}, {% for ... %}, {% namespace::macro(a=a, b=b) %})
45 ///
46 /// - {% set ... %} adds to current frame_context
47 /// - {% for ... %} builds frame_context before iteration
48 /// - {% namespace::macro(a=a, b=b)} builds frame_context before invocation
49 context: FrameContext<'a>,
50 /// Active template for frame
51 pub active_template: &'a Template,
52 /// `ForLoop` if frame is for a for loop
53 pub for_loop: Option<ForLoop<'a>>,
54 /// Macro namespace if MacroFrame
55 pub macro_namespace: Option<&'a str>,
56 }
57
58 impl<'a> StackFrame<'a> {
59 pub fn new(kind: FrameType, name: &'a str, tpl: &'a Template) -> Self {
60 StackFrame {
61 kind,
62 name,
63 context: FrameContext::new(),
64 active_template: tpl,
65 for_loop: None,
66 macro_namespace: None,
67 }
68 }
69
70 pub fn new_for_loop(name: &'a str, tpl: &'a Template, for_loop: ForLoop<'a>) -> Self {
71 StackFrame {
72 kind: FrameType::ForLoop,
73 name,
74 context: FrameContext::new(),
75 active_template: tpl,
76 for_loop: Some(for_loop),
77 macro_namespace: None,
78 }
79 }
80
81 pub fn new_macro(
82 name: &'a str,
83 tpl: &'a Template,
84 macro_namespace: &'a str,
85 context: FrameContext<'a>,
86 ) -> Self {
87 StackFrame {
88 kind: FrameType::Macro,
89 name,
90 context,
91 active_template: tpl,
92 for_loop: None,
93 macro_namespace: Some(macro_namespace),
94 }
95 }
96
97 pub fn new_include(name: &'a str, tpl: &'a Template) -> Self {
98 StackFrame {
99 kind: FrameType::Include,
100 name,
101 context: FrameContext::new(),
102 active_template: tpl,
103 for_loop: None,
104 macro_namespace: None,
105 }
106 }
107
108 /// Finds a value in the stack frame.
109 /// Looks first in `frame_context`, then compares to for_loop key_name and value_name.
110 pub fn find_value(&self, key: &str) -> Option<Val<'a>> {
111 self.find_value_in_frame(key).or_else(|| self.find_value_in_for_loop(key))
112 }
113
114 /// Finds a value in `frame_context`.
115 pub fn find_value_in_frame(&self, key: &str) -> Option<Val<'a>> {
116 if let Some(dot) = key.find('.') {
117 if dot < key.len() + 1 {
118 if let Some(found_value) =
119 self.context.get(&key[0..dot]).map(|v| value_by_pointer(&key[dot + 1..], v))
120 {
121 return found_value;
122 }
123 }
124 } else if let Some(found) = self.context.get(key) {
125 return Some(found.clone());
126 }
127
128 None
129 }
130 /// Finds a value in the `for_loop` if there is one
131 pub fn find_value_in_for_loop(&self, key: &str) -> Option<Val<'a>> {
132 if let Some(ref for_loop) = self.for_loop {
133 // 1st case: the variable is the key of a KeyValue for loop
134 if for_loop.is_key(key) {
135 return Some(Cow::Owned(Value::String(for_loop.get_current_key())));
136 }
137
138 let (real_key, tail) = if let Some(tail_pos) = key.find('.') {
139 (&key[..tail_pos], &key[tail_pos + 1..])
140 } else {
141 (key, "")
142 };
143
144 // 2nd case: one of Tera loop built-in variable
145 if real_key == "loop" {
146 match tail {
147 "index" => {
148 return Some(Cow::Owned(Value::Number((for_loop.current + 1).into())));
149 }
150 "index0" => {
151 return Some(Cow::Owned(Value::Number(for_loop.current.into())));
152 }
153 "first" => {
154 return Some(Cow::Owned(Value::Bool(for_loop.current == 0)));
155 }
156 "last" => {
157 return Some(Cow::Owned(Value::Bool(
158 for_loop.current == for_loop.len() - 1,
159 )));
160 }
161 _ => return None,
162 };
163 }
164
165 // Last case: the variable is/starts with the value name of the for loop
166 // The `set` case will have been taken into account before
167 let v = for_loop.get_current_value();
168 // Exact match to the loop value and no tail
169 if key == for_loop.value_name {
170 return Some(v);
171 }
172
173 if real_key == for_loop.value_name && !tail.is_empty() {
174 return value_by_pointer(tail, &v);
175 }
176 }
177
178 None
179 }
180
181 /// Insert a value in the context
182 pub fn insert(&mut self, key: &'a str, value: Val<'a>) {
183 self.context.insert(key, value);
184 }
185
186 /// Context is cleared on each loop
187 pub fn clear_context(&mut self) {
188 if self.for_loop.is_some() {
189 self.context.clear();
190 }
191 }
192
193 pub fn context_owned(&self) -> HashMap<String, Value> {
194 let mut context = HashMap::new();
195
196 for (key, val) in &self.context {
197 context.insert((*key).to_string(), val.clone().into_owned());
198 }
199
200 context
201 }
202 }