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 iterables_results.iter().for_each(|iterable_result| {
262 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}