1use std::collections::HashMap;
2use std::fmt;
3
4#[derive(Clone, Copy, Debug, PartialEq, Default)]
6pub struct WS {
7    pub left: bool,
9    pub right: bool,
11}
12
13#[derive(Copy, Clone, Debug, PartialEq)]
15pub enum MathOperator {
16    Add,
18    Sub,
20    Mul,
22    Div,
24    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#[derive(Copy, Clone, Debug, PartialEq)]
46pub enum LogicOperator {
47    Gt,
49    Gte,
51    Lt,
53    Lte,
55    Eq,
57    NotEq,
59    And,
61    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#[derive(Clone, Debug, PartialEq)]
86pub struct FunctionCall {
87    pub name: String,
89    pub args: HashMap<String, Expr>,
91}
92
93#[derive(Clone, Debug, PartialEq)]
95pub struct MathExpr {
96    pub lhs: Box<Expr>,
98    pub rhs: Box<Expr>,
100    pub operator: MathOperator,
102}
103
104#[derive(Clone, Debug, PartialEq)]
106pub struct LogicExpr {
107    pub lhs: Box<Expr>,
109    pub rhs: Box<Expr>,
111    pub operator: LogicOperator,
113}
114
115#[derive(Clone, Debug, PartialEq)]
117pub struct StringConcat {
118    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#[derive(Clone, Debug, PartialEq)]
139pub struct In {
140    pub lhs: Box<Expr>,
142    pub rhs: Box<Expr>,
144    pub negated: bool,
146}
147
148#[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    Array(Vec<Expr>),
165    StringConcat(StringConcat),
166    In(In),
167}
168
169#[derive(Clone, Debug, PartialEq)]
172pub struct Expr {
173    pub val: ExprVal,
175    pub negated: bool,
177    pub filters: Vec<FunctionCall>,
179}
180
181impl Expr {
182    pub fn new(val: ExprVal) -> Expr {
184        Expr { val, negated: false, filters: vec![] }
185    }
186
187    pub fn new_negated(val: ExprVal) -> Expr {
189        Expr { val, negated: true, filters: vec![] }
190    }
191
192    pub fn with_filters(val: ExprVal, filters: Vec<FunctionCall>) -> Expr {
194        Expr { val, filters, negated: false }
195    }
196
197    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    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#[derive(Clone, Debug, PartialEq)]
218pub struct Test {
219    pub ident: String,
221    pub negated: bool,
223    pub name: String,
225    pub args: Vec<Expr>,
227}
228
229#[derive(Clone, Debug, PartialEq)]
231pub struct FilterSection {
232    pub filter: FunctionCall,
234    pub body: Vec<Node>,
236}
237
238#[derive(Clone, Debug, PartialEq)]
240pub struct Set {
241    pub key: String,
243    pub value: Expr,
245    pub global: bool,
248}
249
250#[derive(Clone, Debug, PartialEq)]
252pub struct MacroCall {
253    pub namespace: String,
255    pub name: String,
257    pub args: HashMap<String, Expr>,
259}
260
261#[derive(Clone, Debug, PartialEq)]
263pub struct MacroDefinition {
264    pub name: String,
266    pub args: HashMap<String, Option<Expr>>,
268    pub body: Vec<Node>,
270}
271
272#[derive(Clone, Debug, PartialEq)]
274pub struct Block {
275    pub name: String,
277    pub body: Vec<Node>,
279}
280
281#[derive(Clone, Debug, PartialEq)]
283pub struct Forloop {
284    pub key: Option<String>,
286    pub value: String,
288    pub container: Expr,
290    pub body: Vec<Node>,
292    pub empty_body: Option<Vec<Node>>,
294}
295
296#[derive(Clone, Debug, PartialEq)]
298pub struct If {
299    pub conditions: Vec<(WS, Expr, Vec<Node>)>,
301    pub otherwise: Option<(WS, Vec<Node>)>,
303}
304
305#[derive(Clone, Debug, PartialEq)]
307pub enum Node {
308    Super,
310
311    Text(String),
313    VariableBlock(WS, Expr),
315    MacroDefinition(WS, MacroDefinition, WS),
317
318    Extends(WS, String),
320    Include(WS, Vec<String>, bool),
322    ImportMacro(WS, String, String),
324    Set(WS, Set),
326
327    Raw(WS, String, WS),
329
330    FilterSection(WS, FilterSection, WS),
332    Block(WS, Block, WS),
334    Forloop(WS, Forloop, WS),
336
337    If(If, WS),
339
340    Break(WS),
342    Continue(WS),
344
345    Comment(WS, String),
347}