tera/parser/
ast.rs

1use std::collections::HashMap;
2use std::fmt;
3
4/// Whether to remove the whitespace of a `{% %}` tag
5#[derive(Clone, Copy, Debug, PartialEq, Default)]
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)]
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)]
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    pub(crate) fn to_template_string(&self) -> String {
124        let mut res = Vec::new();
125        for value in &self.values {
126            match value {
127                ExprVal::String(ref s) => res.push(format!("'{}'", s)),
128                ExprVal::Ident(ref s) => res.push(s.to_string()),
129                _ => res.push("unknown".to_string()),
130            }
131        }
132
133        res.join(" ~ ")
134    }
135}
136
137/// Something that checks whether the left side is contained in the right side
138#[derive(Clone, Debug, PartialEq)]
139pub struct In {
140    /// The needle, a string or a basic expression/literal
141    pub lhs: Box<Expr>,
142    /// The haystack, can be a string, an array or an ident only currently
143    pub rhs: Box<Expr>,
144    /// Is it using `not` as in `b` not in `...`?
145    pub negated: bool,
146}
147
148/// An expression is the node found in variable block, kwargs and conditions.
149#[derive(Clone, Debug, PartialEq)]
150#[allow(missing_docs)]
151pub enum ExprVal {
152    String(String),
153    Int(i64),
154    Float(f64),
155    Bool(bool),
156    Ident(String),
157    Math(MathExpr),
158    Logic(LogicExpr),
159    Test(Test),
160    MacroCall(MacroCall),
161    FunctionCall(FunctionCall),
162    // A vec of Expr, not ExprVal since filters are allowed
163    // on values inside arrays
164    Array(Vec<Expr>),
165    StringConcat(StringConcat),
166    In(In),
167}
168
169/// An expression is a value that can be negated and followed by
170/// optional filters
171#[derive(Clone, Debug, PartialEq)]
172pub struct Expr {
173    /// The expression we are evaluating
174    pub val: ExprVal,
175    /// Is it using `not`?
176    pub negated: bool,
177    /// List of filters used on that value
178    pub filters: Vec<FunctionCall>,
179}
180
181impl Expr {
182    /// Create a new basic Expr
183    pub fn new(val: ExprVal) -> Expr {
184        Expr { val, negated: false, filters: vec![] }
185    }
186
187    /// Create a new negated Expr
188    pub fn new_negated(val: ExprVal) -> Expr {
189        Expr { val, negated: true, filters: vec![] }
190    }
191
192    /// Create a new basic Expr with some filters
193    pub fn with_filters(val: ExprVal, filters: Vec<FunctionCall>) -> Expr {
194        Expr { val, filters, negated: false }
195    }
196
197    /// Check if the expr has a default filter as first filter
198    pub fn has_default_filter(&self) -> bool {
199        if self.filters.is_empty() {
200            return false;
201        }
202
203        self.filters[0].name == "default"
204    }
205
206    /// Check if the last filter is `safe`
207    pub fn is_marked_safe(&self) -> bool {
208        if self.filters.is_empty() {
209            return false;
210        }
211
212        self.filters[self.filters.len() - 1].name == "safe"
213    }
214}
215
216/// A test node `if my_var is odd`
217#[derive(Clone, Debug, PartialEq)]
218pub struct Test {
219    /// Which variable is evaluated
220    pub ident: String,
221    /// Is it using `not`?
222    pub negated: bool,
223    /// Name of the test
224    pub name: String,
225    /// Any optional arg given to the test
226    pub args: Vec<Expr>,
227}
228
229/// A filter section node `{{ filter name(param="value") }} content {{ endfilter }}`
230#[derive(Clone, Debug, PartialEq)]
231pub struct FilterSection {
232    /// The filter call itsel
233    pub filter: FunctionCall,
234    /// The filter body
235    pub body: Vec<Node>,
236}
237
238/// Set a variable in the context `{% set val = "hey" %}`
239#[derive(Clone, Debug, PartialEq)]
240pub struct Set {
241    /// The name for that value in the context
242    pub key: String,
243    /// The value to assign
244    pub value: Expr,
245    /// Whether we want to set the variable globally or locally
246    /// global_set is only useful in loops
247    pub global: bool,
248}
249
250/// A call to a namespaced macro `macros::my_macro()`
251#[derive(Clone, Debug, PartialEq)]
252pub struct MacroCall {
253    /// The namespace we're looking for that macro in
254    pub namespace: String,
255    /// The macro name
256    pub name: String,
257    /// The args for that macro: name -> value
258    pub args: HashMap<String, Expr>,
259}
260
261/// A Macro definition
262#[derive(Clone, Debug, PartialEq)]
263pub struct MacroDefinition {
264    /// The macro name
265    pub name: String,
266    /// The args for that macro: name -> optional default value
267    pub args: HashMap<String, Option<Expr>>,
268    /// The macro content
269    pub body: Vec<Node>,
270}
271
272/// A block definition
273#[derive(Clone, Debug, PartialEq)]
274pub struct Block {
275    /// The block name
276    pub name: String,
277    /// The block content
278    pub body: Vec<Node>,
279}
280
281/// A forloop: can be over values or key/values
282#[derive(Clone, Debug, PartialEq)]
283pub struct Forloop {
284    /// Name of the key in the loop (only when iterating on map-like objects)
285    pub key: Option<String>,
286    /// Name of the local variable for the value in the loop
287    pub value: String,
288    /// Expression being iterated on
289    pub container: Expr,
290    /// What's in the forloop itself
291    pub body: Vec<Node>,
292    /// The body to execute in case of an empty object
293    pub empty_body: Option<Vec<Node>>,
294}
295
296/// An if/elif/else condition with their respective body
297#[derive(Clone, Debug, PartialEq)]
298pub struct If {
299    /// First item if the if, all the ones after are elif
300    pub conditions: Vec<(WS, Expr, Vec<Node>)>,
301    /// The optional `else` block
302    pub otherwise: Option<(WS, Vec<Node>)>,
303}
304
305/// All Tera nodes that can be encountered
306#[derive(Clone, Debug, PartialEq)]
307pub enum Node {
308    /// A call to `{{ super() }}` in a block
309    Super,
310
311    /// Some actual text
312    Text(String),
313    /// A `{{ }}` block
314    VariableBlock(WS, Expr),
315    /// A `{% macro hello() %}...{% endmacro %}`
316    MacroDefinition(WS, MacroDefinition, WS),
317
318    /// The `{% extends "blabla.html" %}` node, contains the template name
319    Extends(WS, String),
320    /// The `{% include "blabla.html" %}` node, contains the template name
321    Include(WS, Vec<String>, bool),
322    /// The `{% import "macros.html" as macros %}`
323    ImportMacro(WS, String, String),
324    /// The `{% set val = something %}` tag
325    Set(WS, Set),
326
327    /// The text between `{% raw %}` and `{% endraw %}`
328    Raw(WS, String, WS),
329
330    /// A filter section node `{{ filter name(param="value") }} content {{ endfilter }}`
331    FilterSection(WS, FilterSection, WS),
332    /// A `{% block name %}...{% endblock %}`
333    Block(WS, Block, WS),
334    /// A `{% for i in items %}...{% endfor %}`
335    Forloop(WS, Forloop, WS),
336
337    /// A if/elif/else block, WS for the if/elif/else is directly in the struct
338    If(If, WS),
339
340    /// The `{% break %}` tag
341    Break(WS),
342    /// The `{% continue %}` tag
343    Continue(WS),
344
345    /// The `{# #} `comment tag and its content
346    Comment(WS, String),
347}