]>
git.proxmox.com Git - rustc.git/blob - vendor/tera/src/renderer/stack_frame.rs
2 use std
::collections
::HashMap
;
6 use crate::context
::get_json_pointer
;
7 use crate::renderer
::for_loop
::ForLoop
;
8 use crate::template
::Template
;
10 pub type Val
<'a
> = Cow
<'a
, Value
>;
11 pub type FrameContext
<'a
> = HashMap
<&'a
str, Val
<'a
>>;
13 /// Gets a value within a value by pointer, keeping lifetime
15 pub fn value_by_pointer
<'a
>(pointer
: &str, val
: &Val
<'a
>) -> Option
<Val
<'a
>> {
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()))
24 /// Enumerates the types of stack frames
25 #[derive(Clone, Copy, Debug, PartialEq)]
29 /// New frame for macro call
31 /// New frame for for loop
37 /// Entry in the stack frame
39 pub struct StackFrame
<'a
> {
40 /// Type of stack frame
42 /// Frame name for context/debugging
44 /// Assigned value (via {% set ... %}, {% for ... %}, {% namespace::macro(a=a, b=b) %})
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>,
58 impl<'a
> StackFrame
<'a
> {
59 pub fn new(kind
: FrameType
, name
: &'a
str, tpl
: &'a Template
) -> Self {
63 context
: FrameContext
::new(),
66 macro_namespace
: None
,
70 pub fn new_for_loop(name
: &'a
str, tpl
: &'a Template
, for_loop
: ForLoop
<'a
>) -> Self {
72 kind
: FrameType
::ForLoop
,
74 context
: FrameContext
::new(),
76 for_loop
: Some(for_loop
),
77 macro_namespace
: None
,
84 macro_namespace
: &'a
str,
85 context
: FrameContext
<'a
>,
88 kind
: FrameType
::Macro
,
93 macro_namespace
: Some(macro_namespace
),
97 pub fn new_include(name
: &'a
str, tpl
: &'a Template
) -> Self {
99 kind
: FrameType
::Include
,
101 context
: FrameContext
::new(),
102 active_template
: tpl
,
104 macro_namespace
: None
,
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
))
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
))
124 } else if let Some(found
) = self.context
.get(key
) {
125 return Some(found
.clone());
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())));
138 let (real_key
, tail
) = if let Some(tail_pos
) = key
.find('
.'
) {
139 (&key
[..tail_pos
], &key
[tail_pos
+ 1..])
144 // 2nd case: one of Tera loop built-in variable
145 if real_key
== "loop" {
148 return Some(Cow
::Owned(Value
::Number((for_loop
.current
+ 1).into())));
151 return Some(Cow
::Owned(Value
::Number(for_loop
.current
.into())));
154 return Some(Cow
::Owned(Value
::Bool(for_loop
.current
== 0)));
157 return Some(Cow
::Owned(Value
::Bool(
158 for_loop
.current
== for_loop
.len() - 1,
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
{
173 if real_key
== for_loop
.value_name
&& !tail
.is_empty() {
174 return value_by_pointer(tail
, &v
);
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
);
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();
193 pub fn context_owned(&self) -> HashMap
<String
, Value
> {
194 let mut context
= HashMap
::new();
196 for (key
, val
) in &self.context
{
197 context
.insert((*key
).to_string(), val
.clone().into_owned());