template_cli/
evaluate.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4use log::info;
5use pest::iterators::{Pair, Pairs};
6use pest::Parser;
7use serde_json::{json, Map, Value};
8
9use crate::error::TemplateRenderError;
10use crate::function;
11
12#[derive(Parser)]
13#[grammar = "grammar/template.pest"]
14pub struct TemplateParser;
15
16fn format_string(value: &Value) -> String {
17    match value {
18        Value::Null => "".to_string(),
19        Value::Bool(boolean) => boolean.to_string(),
20        Value::Number(number) => number.to_string(),
21        Value::String(string) => string.to_string(),
22        Value::Array(values) => format!("[{}]", values.iter().map(|v| format_string(v)).reduce(|cur, next| format!("{},{}", &cur, &next)).unwrap_or("".to_string())),
23        Value::Object(object) => format!("{{{}}}", object.iter().map(|(k, v)| format!("{}:{}", k, format_string(v))).reduce(|cur, next| format!("{},{}", &cur, &next)).unwrap_or("".to_string())),
24    }
25}
26
27fn parse_literal(value: &Value, literal: &Pair<Rule>) -> Result<Value, TemplateRenderError> {
28    let content = literal.as_str().to_string();
29    match literal.as_rule() {
30        Rule::null => Ok(Value::Null),
31        Rule::boolean => content.parse::<bool>()
32            .map_err(|_err| TemplateRenderError::LiteralParseError(content))
33            .map(|result| Value::from(result)),
34        Rule::number => match literal.clone().into_inner().next().unwrap().as_rule() {
35            Rule::integer_number => content.parse::<i64>()
36                .map_err(|_err| TemplateRenderError::LiteralParseError(content))
37                .map(|result| Value::from(result)),
38            Rule::floating_point_number => content.parse::<f64>()
39                .map_err(|_err| TemplateRenderError::LiteralParseError(content))
40                .map(|result| Value::from(result)),
41            _ => unreachable!()
42        }
43        Rule::string => content.parse::<String>()
44            .map_err(|_err| TemplateRenderError::LiteralParseError(content))
45            .map(|result| Value::from(&result[1..result.len() - 1])),
46        Rule::array => {
47            let array: Result<Vec<Value>, TemplateRenderError> = literal.clone().into_inner()
48                .into_iter()
49                .map(|inner_literal| parse_expression(&value, &mut inner_literal.into_inner()))
50                .collect();
51
52            array.map(|result| Value::Array(result))
53        }
54        Rule::dictionary => {
55            let mut result = Map::new();
56            for pair in literal.clone().into_inner() {
57                let mut key_value_content = pair.into_inner();
58                let pair_key = key_value_content.next().unwrap().as_str();
59                let pair_value = key_value_content.next().unwrap();
60                let evaluated_pair_value = parse_expression(&value, &mut pair_value.into_inner())?;
61
62                result.insert(pair_key.to_string(), evaluated_pair_value);
63            }
64
65            Ok(Value::Object(result))
66        }
67        _ => unreachable!()
68    }
69}
70
71fn parse_expression(value: &Value, expression: &mut Pairs<Rule>) -> Result<Value, TemplateRenderError> {
72    let properties_or_literal = expression.next().unwrap();
73
74    let current_value = match properties_or_literal.as_rule() {
75        Rule::literal => {
76            parse_literal(&value, &properties_or_literal.into_inner().next().unwrap())?
77        }
78        Rule::properties => {
79            let mut current_value = value.clone();
80            for property in properties_or_literal.into_inner() {
81                match property.as_rule() {
82                    Rule::property => {
83                        current_value = current_value[property.as_str()].clone();
84                    }
85                    _ => unreachable!(),
86                }
87            }
88            current_value
89        }
90        _ => unreachable!()
91    };
92
93    let mut result = current_value;
94    for function in expression {
95        match function.as_rule() {
96            Rule::function_call => {
97                let mut function_and_arguments = function.into_inner();
98                let function_name = function_and_arguments.next().unwrap().as_str();
99                let mut arguments: Vec<Value> = vec![];
100                for argument in function_and_arguments {
101                    match argument.as_rule() {
102                        Rule::expression => {
103                            arguments.push(parse_expression(value, &mut argument.into_inner())?)
104                        }
105                        _ => unreachable!(),
106                    }
107                }
108
109                result = function::apply_function(&result, function_name, &arguments)?;
110            }
111            _ => unreachable!(),
112        }
113    }
114    return Ok(result);
115}
116
117
118fn evaluate_template(data: &Value, record: Pair<Rule>) -> Result<String, TemplateRenderError> {
119    let mut result = String::new();
120
121    let mut inner_rules = record.into_inner();
122    let expression = inner_rules.next().unwrap();
123
124    match expression.as_rule() {
125        Rule::if_elif_else_template => {
126            let mut done = false;
127            let mut valid = false;
128            for if_inner in expression.into_inner() {
129                match if_inner.as_rule() {
130                    Rule::if_template => {
131                        valid = false;
132
133                        let mut if_inner_expression = if_inner.into_inner();
134                        let if_keyword = if_inner_expression.next().unwrap();
135                        let invert = match if_keyword.as_rule() {
136                            Rule::keyword_if => false,
137                            Rule::keyword_unless => true,
138                            _ => unreachable!(),
139                        };
140                        let if_expression = if_inner_expression.next().unwrap();
141                        let if_result = parse_expression(&data, &mut if_expression.into_inner())
142                            .map(|value| function::to_boolean(&value))
143                            .unwrap_or(false);
144                        if if_result ^ invert {
145                            done = true;
146                            valid = true
147                        }
148                    }
149                    Rule::elif_template => {
150                        result = result.trim_end_matches(&[' ', '\t']).to_string();
151                        valid = false;
152
153                        let mut elif_inner_expression = if_inner.into_inner();
154                        let elif_expression = elif_inner_expression.next().unwrap();
155                        let elif_result = parse_expression(&data, &mut elif_expression.into_inner())
156                            .map(|value| function::to_boolean(&value))
157                            .unwrap_or(false);
158                        if !done && elif_result {
159                            done = true;
160                            valid = true
161                        }
162                    }
163                    Rule::else_template => {
164                        result = result.trim_end_matches(&[' ', '\t']).to_string();
165                        valid = !done;
166                    }
167                    Rule::end_template => {
168                        result = result.trim_end_matches(&[' ', '\t']).to_string();
169                        valid = false;
170                        done = true;
171                    }
172                    Rule::character => {
173                        if valid {
174                            result.push_str(if_inner.as_str())
175                        }
176                    }
177                    Rule::template => {
178                        if valid {
179                            let evaluation = evaluate_template(&data, if_inner)?;
180                            result.push_str(evaluation.as_str())
181                        }
182                    }
183                    _ => unreachable!(),
184                }
185            }
186        }
187        Rule::for_else_template => {
188            let mut valid = false;
189            let mut done = false;
190            let mut iterable_name: &str = "";
191            let mut iterables: Vec<Value> = vec![];
192            let mut iterables_results: Vec<Rc<RefCell<String>>> = vec![];
193
194            for for_inner in expression.into_inner() {
195                match for_inner.as_rule() {
196                    Rule::for_template => {
197                        let mut for_inner_expression = for_inner.into_inner();
198                        iterable_name = for_inner_expression.next().unwrap().as_str();
199                        let for_iterable = parse_expression(&data, &mut for_inner_expression.next().unwrap().into_inner()).unwrap();
200                        iterables = match for_iterable {
201                            Value::Array(items) => items,
202                            _ => vec![],
203                        };
204                        done = iterables.is_empty();
205                        valid = !iterables.is_empty();
206                        iterables_results = iterables.iter().map(|_| Rc::new(RefCell::new(String::new()))).collect();
207                    }
208                    Rule::else_template => {
209                        result = result.trim_end_matches(&[' ', '\t']).to_string();
210                        valid = false;
211                        if !done {
212                            valid = true
213                        }
214                    }
215                    Rule::end_template => {
216                        result = result.trim_end_matches(&[' ', '\t']).to_string();
217                        valid = false;
218                        done = true;
219                    }
220                    Rule::character => {
221                        if valid {
222                            iterables_results.iter_mut().for_each(|iterables_result| {
223                                iterables_result.replace_with(|r| format!("{}{}", r, for_inner.as_str()));
224                            })
225                        }
226                    }
227                    Rule::template => {
228                        let zipped = iterables.iter().zip(iterables_results.iter());
229                        for (index, (iterable, iterable_result)) in zipped.enumerate() {
230                            let context_value = match data {
231                                Value::Object(map) => {
232                                    let mut q = map.clone();
233                                    q.insert(iterable_name.to_string(), iterable.clone());
234                                    let first = index == 0;
235                                    let last = index == iterables.len() - 1;
236                                    let index0 = index;
237                                    let index1 = index + 1;
238                                    let size = iterables.len();
239                                    q.insert("loop".to_string(), json!({
240                                        "first": first,
241                                        "last": last,
242                                        "index0": index0,
243                                        "index1": index1,
244                                        "size": size,
245                                    }));
246                                    Value::Object(q)
247                                }
248                                _ => iterable.clone(),
249                            };
250                            let template_result = evaluate_template(&context_value, for_inner.clone())?;
251                            iterable_result.replace_with(|current_result|
252                                format!("{}{}", current_result, template_result)
253                            );
254                        }
255                    }
256                    _ => unreachable!(),
257                }
258            }
259
260            // Provide the looped results
261            iterables_results.iter().for_each(|iterable_result| {
262                // eprintln!("{}", iterable_result.take().as_str());
263                result.push_str(iterable_result.take().as_str())
264            }
265            )
266        }
267        Rule::with_template => {
268            let mut name: &str = "";
269            let mut value: Value = Value::Null;
270
271            for for_inner in expression.into_inner() {
272                match for_inner.as_rule() {
273                    Rule::property => {
274                        name = for_inner.as_str();
275                    }
276                    Rule::expression => {
277                        value = parse_expression(&data, &mut for_inner.into_inner())?;
278                    }
279                    Rule::end_template => {
280                        result = result.trim_end_matches(&[' ', '\t']).to_string();
281                    }
282                    Rule::character => {
283                        result.push_str(for_inner.as_str())
284                    }
285                    Rule::template => {
286                        let context_value = match data {
287                            Value::Object(map) => {
288                                let mut q = map.clone();
289                                q.insert(name.to_string(), value.clone());
290                                Value::Object(q)
291                            }
292                            _ => value.clone(),
293                        };
294                        let template_result = evaluate_template(&context_value, for_inner.clone())?;
295                        result.push_str(template_result.as_str());
296                    }
297                    _ => unreachable!(),
298                }
299            }
300        }
301        Rule::debug_template => {
302            let debug_expression = expression.into_inner().next().unwrap();
303            let debug_evaluated = parse_expression(&data, &mut debug_expression.clone().into_inner())?;
304            info!("{}", format!("Debug expression: {} = {}", debug_expression.as_str().trim(), debug_evaluated))
305        }
306        Rule::expression_template => {
307            let mut inner_rules = expression.into_inner();
308            let expression = inner_rules.next().unwrap();
309            let evaluation_result = parse_expression(&data, &mut expression.into_inner())?;
310            result.push_str(format_string(&evaluation_result).to_string().as_str())
311        }
312        Rule::comment => (),
313        _ => unreachable!(),
314    }
315
316    return Ok(result);
317}
318
319pub fn evaluate_file(data: &Value, file: Pair<Rule>) -> Result<String, TemplateRenderError> {
320    let mut result = String::new();
321
322    for record in file.into_inner() {
323        match record.as_rule() {
324            Rule::template => {
325                let evaluation = evaluate_template(&data, record)?;
326                result.push_str(evaluation.as_str())
327            }
328            Rule::character => {
329                result.push_str(record.as_str())
330            }
331            Rule::EOI => (),
332            _ => unreachable!(),
333        }
334    }
335
336    return Ok(result);
337}
338
339pub fn parse_template(template_content: &String) -> Result<Pairs<Rule>, pest::error::Error<Rule>> {
340    TemplateParser::parse(Rule::file, &template_content)
341}