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}