1use std::ops::Index;
2
3use chrono::{DateTime, Local};
4use itertools::Itertools;
5use regex::Regex;
6use serde_json::{Map, Value};
7
8use crate::error::TemplateRenderError;
9
10fn type_of<T>(_: &T) -> String {
11 format!("{}", std::any::type_name::<T>())
12}
13
14pub fn to_boolean(value: &Value) -> bool {
15 match value {
16 Value::Null => false,
17 Value::Bool(bool) => *bool,
18 Value::Number(number) => {
19 if number.is_i64() {
20 number.as_i64().unwrap() != 0
21 } else if number.is_u64() {
22 number.as_u64().unwrap() != 0
23 } else if number.is_f64() {
24 number.as_f64().unwrap() != 0.0
25 } else {
26 unreachable!();
27 }
28 }
29 Value::String(string) => !string.trim().is_empty(),
30 Value::Array(items) => !items.is_empty(),
31 Value::Object(object) => !object.is_empty(),
32 }
33}
34
35fn kebab_case(value: &String) -> String {
36 let re = Regex::new(r"[^a-zA-Z0-9_]+").unwrap();
37 let lower = value.to_lowercase();
38 return re.replace_all(lower.as_str(), "-").to_string();
39}
40
41fn snake_case(value: &String) -> String {
42 let re = Regex::new(r"[^a-zA-Z0-9-]+").unwrap();
43 let lower = value.to_lowercase();
44 return re.replace_all(lower.as_str(), "_").to_string();
45}
46
47fn camel_case(value: &String) -> String {
48 let mut result = String::new();
49 let mut to_upper = false;
50 for c in value.chars() {
51 if c.is_alphanumeric() {
52 result.push(if to_upper { c.to_ascii_uppercase() } else { c });
53 to_upper = false;
54 } else {
55 to_upper = true;
56 }
57 }
58 return result;
59}
60
61fn pascal_case(value: &String) -> String {
62 let mut result = String::new();
63 let mut to_upper = true;
64 for c in value.chars() {
65 if c.is_alphanumeric() {
66 result.push(if to_upper { c.to_ascii_uppercase() } else { c });
67 to_upper = false;
68 } else {
69 to_upper = true;
70 }
71 }
72 return result;
73}
74
75fn capitalize(value: &String) -> String {
76 if value.is_empty() {
77 value.as_str().to_string()
78 } else {
79 let (first, rest) = value.split_at(1);
80 format!("{}{}", first.to_uppercase(), rest)
81 }
82}
83
84fn capitalize_words(value: &String) -> String {
85 let mut result = String::new();
86 let mut to_upper = true;
87 for c in value.chars() {
88 if c.is_whitespace() {
89 to_upper = true;
90 result.push(c);
91 } else {
92 result.push(if to_upper { c.to_ascii_uppercase() } else { c });
93 to_upper = false;
94 }
95 }
96 return result;
97}
98
99fn environment(value: &String) -> Option<String> {
100 return std::env::var(value.as_str()).ok();
101}
102
103fn default(value: &Value, default: &Value) -> Value {
104 if to_boolean(value) {
105 value.clone()
106 } else {
107 default.clone()
108 }
109}
110
111fn require_string_value(value: &Value) -> Result<&String, TemplateRenderError> {
112 match value {
113 Value::String(string) => Ok(string),
114 _ => Err(TemplateRenderError::TypeError(type_of(&value)))
115 }
116}
117
118fn require_u64_value(value: &Value) -> Result<u64, TemplateRenderError> {
119 value.as_u64()
120 .ok_or_else(|| TemplateRenderError::TypeError(type_of(&value)))
121}
122
123fn require_array_value(value: &Value) -> Result<&Vec<Value>, TemplateRenderError> {
124 value.as_array()
125 .ok_or_else(|| TemplateRenderError::TypeError(type_of(&value)))
126}
127
128fn require_object_value(value: &Value) -> Result<&Map<String, Value>, TemplateRenderError> {
129 value.as_object()
130 .ok_or_else(|| TemplateRenderError::TypeError(type_of(&value)))
131}
132
133fn require_argument<'a>(function: &'a str, arguments: &'a Vec<Value>, index: usize) -> Result<&'a Value, TemplateRenderError> {
134 arguments.get(index)
135 .ok_or_else(||
136 TemplateRenderError::RequiredArgumentMissing(format!("Argument {} is missing for function '{}'", index + 1, function.to_string()))
137 )
138}
139
140pub fn apply_function(value: &Value, function: &str, arguments: &Vec<Value>) -> Result<Value, TemplateRenderError> {
141 return match function {
142 "lowerCase" => {
143 let string = require_string_value(value)?;
144 Ok(Value::String(string.to_lowercase()))
145 }
146 "upperCase" => {
147 let string = require_string_value(value)?;
148 Ok(Value::String(string.to_uppercase()))
149 }
150 "kebabCase" => {
151 let string = require_string_value(value)?;
152 Ok(Value::String(kebab_case(string)))
153 }
154 "snakeCase" => {
155 let string = require_string_value(value)?;
156 Ok(Value::String(snake_case(string)))
157 }
158 "camelCase" => {
159 let string = require_string_value(value)?;
160 Ok(Value::String(camel_case(string)))
161 }
162 "pascalCase" => {
163 let string = require_string_value(value)?;
164 Ok(Value::String(pascal_case(string)))
165 }
166 "capitalize" => {
167 let string = require_string_value(value)?;
168 Ok(Value::String(capitalize(string)))
169 }
170 "capitalizeWords" => {
171 let string = require_string_value(value)?;
172 Ok(Value::String(capitalize_words(string)))
173 }
174 "length" => {
175 match value {
176 Value::String(string) => Ok(Value::from(string.len())),
177 Value::Array(array) => Ok(Value::from(array.len())),
178 Value::Object(dictionary) => Ok(Value::from(dictionary.len())),
179 _ => Err(TemplateRenderError::TypeError(type_of(&value)))
180 }
181 }
182 "environment" => {
183 let string = require_string_value(value)?;
184 Ok(environment(string)
185 .map(|value| Value::from(value))
186 .unwrap_or(Value::Null))
187 }
188 "default" => {
189 let default_value = require_argument(function, arguments, 0)?;
190 Ok(default(value, &default_value))
191 }
192 "coalesce" => {
193 let default_value = require_argument(function, arguments, 0)?;
194 let result = match value {
195 Value::Null => default_value,
196 _ => value,
197 };
198 Ok(result.clone())
199 }
200 "reverse" => {
201 match value {
202 Value::String(string) => Ok(Value::String(String::from_iter(string.chars().rev()))),
203 Value::Array(array) => {
204 let mut reverted = array.clone();
205 reverted.reverse();
206 Ok(Value::Array(reverted))
207 }
208 _ => Err(TemplateRenderError::TypeError(type_of(&value)))
209 }
210 }
211 "split" => {
212 let string = require_string_value(value)?;
213 let splitter = require_argument(function, arguments, 0)?;
214 let splitter_string = require_string_value(splitter)?;
215 let split_strings = string.split(splitter_string).map(|split| Value::String(split.to_string())).collect();
216 Ok(Value::Array(split_strings))
217 }
218 "lines" => {
219 let string = require_string_value(value)?;
220 let lines: Vec<Value> = string.trim().lines().map(|item| Value::String(item.to_string())).collect();
221 Ok(Value::Array(lines))
222 }
223 "matches" => {
224 let string = require_string_value(value)?;
225 let regex = require_argument(function, arguments, 0)?;
226 let regex_string = require_string_value(regex)?;
227 let re = Regex::new(regex_string).map_err(|_err| TemplateRenderError::InvalidRegexError(regex_string.to_string()))?;
228 Ok(Value::Bool(re.is_match(string.as_str())))
229 }
230 "substring" => {
231 let string = require_string_value(value)?;
232 let from = require_argument(function, arguments, 0)?;
233 let from_value = require_u64_value(from)?;
234 let to = arguments.get(1);
235 if to.is_some() {
236 let to_value = require_u64_value(to.unwrap())?;
237 Ok(Value::String(string[usize::try_from(from_value).unwrap().max(0).min(string.len())..usize::try_from(to_value).unwrap().max(0).min(string.len())].to_string()))
238 } else {
239 Ok(Value::String(string[usize::try_from(from_value).unwrap().max(0).min(string.len())..].to_string()))
240 }
241 }
242 "take" => {
243 let n = require_argument(function, arguments, 0)?;
244 let n_value = require_u64_value(n)?;
245 let to_index = usize::try_from(n_value).unwrap();
246 match value {
247 Value::String(string) => Ok(Value::String(string[..to_index.max(0).min(string.len())].to_string())),
248 Value::Array(array) => Ok(Value::Array(array[..to_index.max(0).min(array.len())].to_vec())),
249 _ => Err(TemplateRenderError::TypeError(type_of(&value)))
250 }
251 }
252 "drop" => {
253 let n = require_argument(function, arguments, 0)?;
254 let n_value = require_u64_value(n)?;
255 let from_index = usize::try_from(n_value).unwrap();
256 match value {
257 Value::String(string) => Ok(Value::String(string[from_index.max(0).min(string.len())..].to_string())),
258 Value::Array(array) => Ok(Value::Array(array[from_index.max(0).min(array.len())..].to_vec())),
259 _ => Err(TemplateRenderError::TypeError(type_of(&value)))
260 }
261 }
262 "first" => {
263 let array = require_array_value(value)?;
264 Ok(array.first().unwrap_or(&Value::Null).clone())
265 }
266 "last" => {
267 let array = require_array_value(value)?;
268 Ok(array.last().unwrap_or(&Value::Null).clone())
269 }
270 "index" => {
271 let index = require_argument(function, arguments, 0)?;
272 let index_number = require_u64_value(index)?;
273 let array = require_array_value(value)?;
274 let result = if index_number < array.len() as u64 {
275 array.index(index_number as usize).clone()
276 } else {
277 Value::Null
278 };
279 Ok(result)
280 }
281 "contains" => {
282 let needle = require_argument(function, arguments, 0)?;
283 match value {
284 Value::String(substring) => {
285 let needle_string = require_string_value(needle)?;
286 Ok(Value::Bool(substring.contains(needle_string)))
287 }
288 Value::Array(array) => Ok(Value::Bool(array.contains(needle))),
289 _ => Err(TemplateRenderError::TypeError(type_of(&needle))),
290 }
291 }
292 "containsKey" => {
293 let key = require_argument(function, arguments, 0)?;
294 let object = require_object_value(value)?;
295 let key_value = require_string_value(key)?;
296 Ok(Value::Bool(object.contains_key(key_value)))
297 }
298 "containsValue" => {
299 let needle = require_argument(function, arguments, 0)?;
300 let object = require_object_value(value)?;
301 Ok(Value::Bool(object.values().any(|val| val == needle)))
302 }
303 "startsWith" => {
304 let string = require_string_value(value)?;
305 let start = require_argument(function, arguments, 0)?;
306 let start_string = require_string_value(start)?;
307 Ok(Value::Bool(string.starts_with(start_string)))
308 }
309 "endsWith" => {
310 let string = require_string_value(value)?;
311 let end = require_argument(function, arguments, 0)?;
312 let end_string = require_string_value(end)?;
313 Ok(Value::Bool(string.ends_with(end_string)))
314 }
315 "empty" => {
316 Ok(Value::Bool(!to_boolean(value)))
317 }
318 "unique" => {
319 let array = require_array_value(value)?;
320 let unique = array.clone().into_iter()
321 .unique_by(|item| format!("{item}"))
322 .collect::<Vec<_>>();
323 Ok(Value::Array(unique))
324 }
325 "keys" => {
326 let object = require_object_value(value)?;
327 let keys = object.keys().into_iter().map(|key| Value::String(key.clone())).collect::<Vec<_>>();
328 Ok(Value::Array(keys))
329 }
330 "values" => {
331 let object = require_object_value(value)?;
332 Ok(Value::Array(object.values().cloned().into_iter().collect::<Vec<_>>()))
333 }
334 "invert" => {
335 let object = require_object_value(value)?;
336 if let Some(item) = object.values().into_iter().find(|value| !value.is_string()) {
337 Err(TemplateRenderError::TypeError(type_of(&item)))
338 } else {
339 let inverted = object.clone().into_iter().map(|(key, value)| (value.as_str().unwrap().to_string(), Value::String(key))).collect();
340 Ok(Value::Object(inverted))
341 }
342 }
343 "toJson" => {
344 let result = serde_json::to_string(value)
345 .map_err(|_| TemplateRenderError::JsonSerializationError)?;
346 Ok(Value::String(result))
347 }
348 "toPrettyJson" => {
349 let result = serde_json::to_string_pretty(value)
350 .map_err(|_| TemplateRenderError::JsonSerializationError)?;
351 Ok(Value::String(result))
352 }
353 "fromJson" => {
354 let string = require_string_value(value)?;
355 serde_json::from_str(string.as_str())
356 .map_err(|error| TemplateRenderError::JsonParseError(error.to_string()))
357 }
358 "abbreviate" => {
359 let n = require_argument(function, arguments, 0)?;
360 let string = require_string_value(value)?;
361 let n_value = require_u64_value(n)?;
362 if string.len() <= n_value as usize {
363 Ok(Value::String(string.clone()))
364 } else {
365 Ok(Value::String(format!("{}…", string.as_str()[0..((n_value.max(1) as usize) - 1)].to_string())))
366 }
367 }
368 "trimLeft" => {
369 let string = require_string_value(value)?;
370 Ok(Value::String(string.trim_start().to_string()))
371 }
372 "trimRight" => {
373 let string = require_string_value(value)?;
374 Ok(Value::String(string.trim_end().to_string()))
375 }
376 "trim" => {
377 let string = require_string_value(value)?;
378 Ok(Value::String(string.trim().to_string()))
379 }
380 "replace" => {
381 let string = require_string_value(value)?;
382 let search = require_argument(function, arguments, 0)?;
383 let search_string = require_string_value(search)?;
384 let replacement = require_argument(function, arguments, 1)?;
385 let replacement_string = require_string_value(replacement)?;
386 Ok(Value::String(string.replace(search_string, replacement_string)))
387 }
388 "regexReplace" => {
389 let string = require_string_value(value)?;
390 let regex = require_argument(function, arguments, 0)?;
391 let regex_string = require_string_value(regex)?;
392 let parsed_regex = Regex::new(regex_string)
393 .map_err(|_| TemplateRenderError::InvalidRegexError(regex_string.to_string()))?;
394 let replacement = require_argument(function, arguments, 1)?;
395 let replacement_string = require_string_value(replacement)?;
396 Ok(Value::String(parsed_regex.replace_all(string.as_str(), replacement_string).to_string()))
397 }
398 "negate" => {
399 Ok(Value::Bool(!to_boolean(value)))
400 }
401 "all" => {
402 let result = require_array_value(value)?;
403 Ok(Value::Bool(result.into_iter().all(|item| to_boolean(item))))
404 }
405 "any" => {
406 let result = require_array_value(value)?;
407 Ok(Value::Bool(result.into_iter().any(|item| to_boolean(item))))
408 }
409 "none" => {
410 let result = require_array_value(value)?;
411 Ok(Value::Bool(result.into_iter().all(|item| !to_boolean(item))))
412 }
413 "some" => {
414 let result = require_array_value(value)?;
415 Ok(Value::Bool(result.into_iter().any(|item| !to_boolean(item))))
416 }
417 "chunked" => {
418 let array = require_array_value(value)?;
419 let chunk_size = require_argument(function, arguments, 0)?;
420 let chunk_size_number = require_u64_value(chunk_size)? as usize;
421 let overlap = require_argument(function, arguments, 1)?;
422 let overlap_number = require_u64_value(overlap)? as usize;
423
424 if overlap_number >= chunk_size_number {
425 Err(TemplateRenderError::ArgumentValueError(format!("The overlap ({overlap_number}) cannot be equal or larger than the chunk size ({chunk_size_number})")))
426 } else {
427 let mut result: Vec<Value> = vec![];
428
429 for i in (0..(array.len())).step_by(chunk_size_number - overlap_number) {
430 result.push(Value::Array(array[i..(i + chunk_size_number - overlap_number).min(array.len())].to_vec()))
434 }
435 Ok(Value::Array(result))
436 }
437 }
438 "parseFormatDateTime" => {
439 let string = require_string_value(value)?;
440
441 let parse_result = if string == "now" {
442 DateTime::from(Local::now())
444 } else {
445 let parse_format = require_argument(function, arguments, 0)?;
446 let parse_format_string = require_string_value(parse_format)?;
447 DateTime::parse_from_str(string, parse_format_string)
448 .map_err(|err| TemplateRenderError::ArgumentValueError(format!("Could not parse date-time with value '{string}' and parse format string '{parse_format_string}': {err}")))?
449 };
450
451 let format = require_argument(function, arguments, 1)?;
452 let format_string = require_string_value(format)?;
453 let formatted = parse_result.format(format_string).to_string();
454 Ok(Value::String(formatted))
455 }
456 "alternate" => {
457 let index = require_u64_value(value)?;
458 let items = require_argument(function, arguments, 0)?;
459 let items_array = require_array_value(items)?;
460
461 if items_array.is_empty() {
462 Ok(Value::Null)
463 } else {
464 let array_index: usize = index.try_into()
465 .map_err(|error| TemplateRenderError::ArgumentValueError(format!("{} cannot be cast to usize, {}", index, error)))?;
466
467 Ok(items_array[array_index % items_array.len()].clone())
468 }
469 }
470 "assert" => {
471 let expected_value = require_argument(function, arguments, 0)?;
472 let message = require_argument(function, arguments, 1)?;
473 let message_string = require_string_value(message)?;
474
475 if value != expected_value {
476 Err(TemplateRenderError::AssertionError(format!("Expected value '{}' but found '{}': {}", expected_value, value, message_string)))
477 } else {
478 Ok(Value::Null)
479 }
480 }
481 _ => Err(TemplateRenderError::UnknownFunctionError(function.to_string()))
482 };
483}