tera_introspection/parser/ast.rs
1use std::collections::HashMap;
2use std::fmt;
3
4/// Whether to remove the whitespace of a `{% %}` tag
5#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
6pub struct WS {
7 /// `true` if the tag is `{%-`
8 pub left: bool,
9 /// `true` if the tag is `-%}`
10 pub right: bool,
11}
12
13/// All math operators
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15pub enum MathOperator {
16 /// +
17 Add,
18 /// -
19 Sub,
20 /// *
21 Mul,
22 /// /
23 Div,
24 /// %
25 Modulo,
26}
27
28impl fmt::Display for MathOperator {
29 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30 write!(
31 f,
32 "{}",
33 match *self {
34 MathOperator::Add => "+",
35 MathOperator::Sub => "-",
36 MathOperator::Mul => "*",
37 MathOperator::Div => "/",
38 MathOperator::Modulo => "%",
39 }
40 )
41 }
42}
43
44/// All logic operators
45#[derive(Copy, Clone, Debug, PartialEq, Eq)]
46pub enum LogicOperator {
47 /// >
48 Gt,
49 /// >=
50 Gte,
51 /// <
52 Lt,
53 /// <=
54 Lte,
55 /// ==
56 Eq,
57 /// !=
58 NotEq,
59 /// and
60 And,
61 /// or
62 Or,
63}
64
65impl fmt::Display for LogicOperator {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 write!(
68 f,
69 "{}",
70 match *self {
71 LogicOperator::Gt => ">",
72 LogicOperator::Gte => ">=",
73 LogicOperator::Lt => "<",
74 LogicOperator::Lte => "<=",
75 LogicOperator::Eq => "==",
76 LogicOperator::NotEq => "!=",
77 LogicOperator::And => "and",
78 LogicOperator::Or => "or",
79 }
80 )
81 }
82}
83
84/// A function call, can be a filter or a global function
85#[derive(Clone, Debug, PartialEq)]
86pub struct FunctionCall {
87 /// The name of the function
88 pub name: String,
89 /// The args of the function: key -> value
90 pub args: HashMap<String, Expr>,
91}
92
93/// A mathematical expression
94#[derive(Clone, Debug, PartialEq)]
95pub struct MathExpr {
96 /// The left hand side of the expression
97 pub lhs: Box<Expr>,
98 /// The right hand side of the expression
99 pub rhs: Box<Expr>,
100 /// The operator used
101 pub operator: MathOperator,
102}
103
104/// A logical expression
105#[derive(Clone, Debug, PartialEq)]
106pub struct LogicExpr {
107 /// The left hand side of the expression
108 pub lhs: Box<Expr>,
109 /// The right hand side of the expression
110 pub rhs: Box<Expr>,
111 /// The operator used
112 pub operator: LogicOperator,
113}
114
115/// Can only be a combination of string + ident or ident + ident
116#[derive(Clone, Debug, PartialEq)]
117pub struct StringConcat {
118 /// All the values we're concatening into a string
119 pub values: Vec<ExprVal>,
120}
121
122impl StringConcat {
123 #[allow(dead_code)]
124 pub(crate) fn to_template_string(&self) -> String {
125 let mut res = Vec::new();
126 for value in &self.values {
127 match value {
128 ExprVal::String(ref s) => res.push(format!("'{}'", s)),
129 ExprVal::Ident(ref s) => res.push(s.to_string()),
130 _ => res.push("unknown".to_string()),
131 }
132 }
133
134 res.join(" ~ ")
135 }
136}
137
138/// Something that checks whether the left side is contained in the right side
139#[derive(Clone, Debug, PartialEq)]
140pub struct In {
141 /// The needle, a string or a basic expression/literal
142 pub lhs: Box<Expr>,
143 /// The haystack, can be a string, an array or an ident only currently
144 pub rhs: Box<Expr>,
145 /// Is it using `not` as in `b` not in `...`?
146 pub negated: bool,
147}
148
149/// An expression is the node found in variable block, kwargs and conditions.
150#[derive(Clone, Debug, PartialEq)]
151#[allow(missing_docs)]
152pub enum ExprVal {
153 String(String),
154 Int(i64),
155 Float(f64),
156 Bool(bool),
157 Ident(String),
158 Math(MathExpr),
159 Logic(LogicExpr),
160 Test(Test),
161 MacroCall(MacroCall),
162 FunctionCall(FunctionCall),
163 // A vec of Expr, not ExprVal since filters are allowed
164 // on values inside arrays
165 Array(Vec<Expr>),
166 StringConcat(StringConcat),
167 In(In),
168}
169
170/// An expression is a value that can be negated and followed by
171/// optional filters
172#[derive(Clone, Debug, PartialEq)]
173pub struct Expr {
174 /// The expression we are evaluating
175 pub val: ExprVal,
176 /// Is it using `not`?
177 pub negated: bool,
178 /// List of filters used on that value
179 pub filters: Vec<FunctionCall>,
180}
181
182impl Expr {
183 /// Create a new basic Expr
184 pub fn new(val: ExprVal) -> Expr {
185 Expr {
186 val,
187 negated: false,
188 filters: vec![],
189 }
190 }
191
192 /// Create a new negated Expr
193 pub fn new_negated(val: ExprVal) -> Expr {
194 Expr {
195 val,
196 negated: true,
197 filters: vec![],
198 }
199 }
200
201 /// Create a new basic Expr with some filters
202 pub fn with_filters(val: ExprVal, filters: Vec<FunctionCall>) -> Expr {
203 Expr {
204 val,
205 filters,
206 negated: false,
207 }
208 }
209
210 /// Check if the expr has a default filter as first filter
211 pub fn has_default_filter(&self) -> bool {
212 if self.filters.is_empty() {
213 return false;
214 }
215
216 self.filters[0].name == "default"
217 }
218
219 /// Check if the last filter is `safe`
220 pub fn is_marked_safe(&self) -> bool {
221 if self.filters.is_empty() {
222 return false;
223 }
224
225 self.filters[self.filters.len() - 1].name == "safe"
226 }
227}
228
229/// A test node `if my_var is odd`
230#[derive(Clone, Debug, PartialEq)]
231pub struct Test {
232 /// Which variable is evaluated
233 pub ident: String,
234 /// Is it using `not`?
235 pub negated: bool,
236 /// Name of the test
237 pub name: String,
238 /// Any optional arg given to the test
239 pub args: Vec<Expr>,
240}
241
242/// A filter section node `{{ filter name(param="value") }} content {{ endfilter }}`
243#[derive(Clone, Debug, PartialEq)]
244pub struct FilterSection {
245 /// The filter call itsel
246 pub filter: FunctionCall,
247 /// The filter body
248 pub body: Vec<Node>,
249}
250
251/// Set a variable in the context `{% set val = "hey" %}`
252#[derive(Clone, Debug, PartialEq)]
253pub struct Set {
254 /// The name for that value in the context
255 pub key: String,
256 /// The value to assign
257 pub value: Expr,
258 /// Whether we want to set the variable globally or locally
259 /// global_set is only useful in loops
260 pub global: bool,
261}
262
263/// A call to a namespaced macro `macros::my_macro()`
264#[derive(Clone, Debug, PartialEq)]
265pub struct MacroCall {
266 /// The namespace we're looking for that macro in
267 pub namespace: String,
268 /// The macro name
269 pub name: String,
270 /// The args for that macro: name -> value
271 pub args: HashMap<String, Expr>,
272}
273
274/// A Macro definition
275#[derive(Clone, Debug, PartialEq)]
276pub struct MacroDefinition {
277 /// The macro name
278 pub name: String,
279 /// The args for that macro: name -> optional default value
280 pub args: HashMap<String, Option<Expr>>,
281 /// The macro content
282 pub body: Vec<Node>,
283}
284
285/// A block definition
286#[derive(Clone, Debug, PartialEq)]
287pub struct Block {
288 /// The block name
289 pub name: String,
290 /// The block content
291 pub body: Vec<Node>,
292}
293
294/// A forloop: can be over values or key/values
295#[derive(Clone, Debug, PartialEq)]
296pub struct Forloop {
297 /// Name of the key in the loop (only when iterating on map-like objects)
298 pub key: Option<String>,
299 /// Name of the local variable for the value in the loop
300 pub value: String,
301 /// Expression being iterated on
302 pub container: Expr,
303 /// What's in the forloop itself
304 pub body: Vec<Node>,
305 /// The body to execute in case of an empty object
306 pub empty_body: Option<Vec<Node>>,
307}
308
309/// An if/elif/else condition with their respective body
310#[derive(Clone, Debug, PartialEq)]
311pub struct If {
312 /// First item if the if, all the ones after are elif
313 pub conditions: Vec<(WS, Expr, Vec<Node>)>,
314 /// The optional `else` block
315 pub otherwise: Option<(WS, Vec<Node>)>,
316}
317
318/// All Tera nodes that can be encountered
319#[derive(Clone, Debug, PartialEq)]
320pub enum Node {
321 /// A call to `{{ super() }}` in a block
322 Super,
323
324 /// Some actual text
325 Text(String),
326 /// A `{{ }}` block
327 VariableBlock(WS, Expr),
328 /// A `{% macro hello() %}...{% endmacro %}`
329 MacroDefinition(WS, MacroDefinition, WS),
330
331 /// The `{% extends "blabla.html" %}` node, contains the template name
332 Extends(WS, String),
333 /// The `{% include "blabla.html" %}` node, contains the template name
334 Include(WS, Vec<String>, bool),
335 /// The `{% import "macros.html" as macros %}`
336 ImportMacro(WS, String, String),
337 /// The `{% set val = something %}` tag
338 Set(WS, Set),
339
340 /// The text between `{% raw %}` and `{% endraw %}`
341 Raw(WS, String, WS),
342
343 /// A filter section node `{{ filter name(param="value") }} content {{ endfilter }}`
344 FilterSection(WS, FilterSection, WS),
345 /// A `{% block name %}...{% endblock %}`
346 Block(WS, Block, WS),
347 /// A `{% for i in items %}...{% endfor %}`
348 Forloop(WS, Forloop, WS),
349
350 /// A if/elif/else block, WS for the if/elif/else is directly in the struct
351 If(If, WS),
352
353 /// The `{% break %}` tag
354 Break(WS),
355 /// The `{% continue %}` tag
356 Continue(WS),
357
358 /// The `{# #} `comment tag and its content
359 Comment(WS, String),
360}