1use std::ops::{Bound, RangeBounds};
3
4use crate::errors::TransformerError;
5use crate::VAR_TRANSFORM_SEP_CHAR;
6use lazy_static::lazy_static;
7use regex::Regex;
8use titlecase::titlecase;
9
10pub fn apply_tranformers(val: &str, transformations: &str) -> Result<String, TransformerError> {
14 let mut val: String = val.to_string();
15 for tstr in transformations.split(VAR_TRANSFORM_SEP_CHAR) {
16 if tstr.is_empty() {
17 continue;
18 }
19 let (name, args) = tstr.split_once('(').ok_or(TransformerError::InvalidSyntax(
20 tstr.to_string(),
21 "No opening paranthesis".to_string(),
22 ))?;
23 let args: Vec<&str> = args
24 .strip_suffix(')')
25 .ok_or(TransformerError::InvalidSyntax(
26 tstr.to_string(),
27 "No closing paranthesis".to_string(),
28 ))?
29 .split(',')
30 .collect();
31 val = match name {
32 "f" => float_format(&val, args)?,
33 "case" => string_case(&val, args)?,
34 "calc" => calc(&val, args)?,
35 "count" => count(&val, args)?,
36 "repl" => replace(&val, args)?,
37 "take" => take(&val, args)?,
38 "trim" => trim(&val, args)?,
39 "comma" => comma(&val, args)?,
40 "group" => group(&val, args)?,
41 "q" => quote(&val, args)?,
42 _ => {
43 return Err(TransformerError::UnknownTranformer(
44 name.to_string(),
45 val.to_string(),
46 ))
47 }
48 };
49 }
50 Ok(val)
51}
52
53pub fn bound(b: Bound<&usize>, lower: bool) -> Option<usize> {
69 match b {
70 Bound::Unbounded => None,
71 Bound::Included(v) => Some(*v),
72 Bound::Excluded(v) => Some(if lower { v + 1 } else { v - 1 }),
73 }
74}
75
76fn check_arguments_len<R: RangeBounds<usize>>(
78 func_name: &'static str,
79 req: R,
80 given: usize,
81) -> Result<(), TransformerError> {
82 if req.contains(&given) {
83 Ok(())
84 } else {
85 match (
86 bound(req.start_bound(), true),
87 bound(req.end_bound(), false),
88 ) {
89 (None, Some(r)) => Err(TransformerError::TooManyArguments(func_name, r, given)),
90 (Some(r), None) => Err(TransformerError::TooFewArguments(func_name, r, given)),
91 (Some(r1), Some(r2)) => {
92 if given < r1 {
93 Err(TransformerError::TooFewArguments(func_name, r1, given))
94 } else {
95 Err(TransformerError::TooManyArguments(func_name, r2, given))
96 }
97 }
98 _ => Ok(()),
99 }
100 }
101}
102
103pub fn float_format(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
116 let func_name = "f";
117 check_arguments_len(func_name, 1..=1, args.len())?;
118 let format = args[0];
119 let val = val
120 .parse::<f64>()
121 .map_err(|_| TransformerError::InvalidValueType(func_name, "float"))?;
122 let mut start = 0usize;
123 let mut decimal = 6usize;
124 if let Some((d, f)) = format.split_once('.') {
125 if !d.is_empty() {
126 start = d.parse().map_err(|_| {
127 TransformerError::InvalidArgumentType(func_name, d.to_string(), "uint")
128 })?;
129 }
130 if f.is_empty() {
131 decimal = 0;
132 } else {
133 decimal = f.parse().map_err(|_| {
134 TransformerError::InvalidArgumentType(func_name, f.to_string(), "uint")
135 })?;
136 }
137 } else if !format.is_empty() {
138 decimal = format.parse().map_err(|_| {
139 TransformerError::InvalidArgumentType(func_name, format.to_string(), "uint")
140 })?;
141 }
142 Ok(format!("{0:1$.2$}", val, start, decimal))
143}
144
145pub fn string_case(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
159 let func_name = "case";
160 check_arguments_len(func_name, 1..=1, args.len())?;
161 let format = args[0];
162 match format.to_lowercase().as_str() {
163 "up" => Ok(val.to_uppercase()),
164 "down" => Ok(val.to_lowercase()),
165 "title" => Ok(titlecase(val)),
166 "proper" => Ok({
167 let mut c = val.chars();
168 match c.next() {
169 None => String::new(),
170 Some(f) => {
171 f.to_uppercase().collect::<String>() + c.as_str().to_lowercase().as_str()
172 }
173 }
174 }),
175 _ => Err(TransformerError::InvalidArgumentType(
176 func_name,
177 format.to_string(),
178 "{up;down;proper;title}",
179 )),
180 }
181}
182
183lazy_static! {
184 static ref CALC_NUMBERS: Regex = Regex::new("[0-9.]+").unwrap();
185}
186
187pub fn calc(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
200 let func_name = "calc";
201 check_arguments_len(func_name, 1.., args.len())?;
202
203 let val: f64 = val
204 .parse()
205 .map_err(|_| TransformerError::InvalidValueType(func_name, "float"))?;
206 let mut results: Vec<String> = Vec::new();
207 for expr in args {
208 let mut last_match = 0usize;
209 let mut result = val;
210 for cap in CALC_NUMBERS.captures_iter(expr) {
211 let m = cap.get(0).unwrap();
212 let curr_val = m.as_str().parse().map_err(|_| {
213 TransformerError::InvalidArgumentType(func_name, m.as_str().to_string(), "float")
214 })?;
215 if m.start() == 0 {
216 result = curr_val;
217 } else {
218 match &expr[last_match..m.start()] {
219 "+" => result += curr_val,
220 "-" => result -= curr_val,
221 "/" => result /= curr_val,
222 "*" => result *= curr_val,
223 "^" => result = result.powf(curr_val),
224 s => {
225 return Err(TransformerError::InvalidArgumentType(
226 func_name,
227 s.to_string(),
228 "{+,-,*,/,^}",
229 ))
230 }
231 };
232 }
233 last_match = m.end();
234 }
235 results.push(result.to_string());
236 }
237 Ok(results.join(","))
238}
239
240pub fn count(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
254 let func_name = "count";
255 check_arguments_len(func_name, 1.., args.len())?;
256 let counts: Vec<String> = args
257 .iter()
258 .map(|sep| val.matches(sep).count().to_string())
259 .collect();
260 Ok(counts.join(","))
261}
262
263pub fn replace(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
275 let func_name = "replace";
276 check_arguments_len(func_name, 2..=2, args.len())?;
277 Ok(val.replace(args[0], args[1]))
278}
279
280pub fn take(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
297 let func_name = "take";
298 check_arguments_len(func_name, 2..=3, args.len())?;
299 let n: usize = args[1].parse().map_err(|_| {
300 TransformerError::InvalidArgumentType(func_name, args[1].to_string(), "uint")
301 })?;
302 let spl = if args.len() == 2 {
303 val.split(args[0]).nth(n - 1)
304 } else {
305 val.splitn(
306 args[2].parse().map_err(|_| {
307 TransformerError::InvalidArgumentType(func_name, args[1].to_string(), "int")
308 })?,
309 args[0],
310 )
311 .nth(n - 1)
312 };
313
314 Ok(spl.unwrap_or("").to_string())
315}
316
317pub fn trim(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
332 let func_name = "trim";
333 check_arguments_len(func_name, .., args.len())?;
334 if args.is_empty() {
335 return Ok(val.trim().to_string());
336 }
337 let mut val = val;
338 for arg in args {
339 val = val.trim_matches(|c| arg.contains(c))
340 }
341
342 Ok(val.to_string())
343}
344
345pub fn comma(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
360 let func_name = "comma";
361 check_arguments_len(func_name, 1.., args.len())?;
362 let mut args: Vec<usize> = args
363 .iter()
364 .map(|s| {
365 s.parse().map_err(|_| {
366 TransformerError::InvalidArgumentType(func_name, s.to_string(), "uint")
367 })
368 })
369 .rev()
370 .collect::<Result<Vec<usize>, TransformerError>>()?;
371 let last = args[0];
372 let mut i = args.pop().unwrap();
373
374 let mut result = vec![];
375 let val: Vec<char> = val.replace(',', "").chars().rev().collect();
376 for c in val {
377 if i == 0 {
378 i = args.pop().unwrap_or(last);
379 result.push(',');
380 }
381 result.push(c);
382 i -= 1;
383 }
384 result.reverse();
385 let result: String = result.into_iter().collect();
386 Ok(result)
387}
388
389pub fn group(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
404 let func_name = "group";
405 check_arguments_len(func_name, 2.., args.len())?;
406 let sep = args[0];
407 let mut args: Vec<usize> = args[1..]
408 .iter()
409 .map(|s| {
410 s.parse().map_err(|_| {
411 TransformerError::InvalidArgumentType(func_name, s.to_string(), "uint")
412 })
413 })
414 .rev()
415 .collect::<Result<Vec<usize>, TransformerError>>()?;
416 let last = args[0];
417 let mut i = args.pop().unwrap();
418
419 let mut result = vec![];
420 let val: Vec<char> = val.replace(sep, "").chars().rev().collect();
421 for c in val {
422 if i == 0 {
423 i = args.pop().unwrap_or(last);
424 for c in sep.chars().rev() {
425 result.push(c);
426 }
427 }
428 result.push(c);
429 i -= 1;
430 }
431 result.reverse();
432 let result: String = result.into_iter().collect();
433 Ok(result)
434}
435
436pub fn quote(val: &str, args: Vec<&str>) -> Result<String, TransformerError> {
451 let func_name = "quote";
452 check_arguments_len(func_name, ..=2, args.len())?;
453 Ok(if args.is_empty() {
454 format!("{:?}", val)
455 } else if args.len() == 1 {
456 if args[0].is_empty() {
457 format!("{:?}", val)
458 } else {
459 format!(
460 "{0}{1}{0}",
461 args[0],
462 val.replace(args[0], &format!("\\{}", args[0]))
463 )
464 }
465 } else {
466 format!(
467 "{}{}{}",
468 args[0],
469 val.replace(args[0], &format!("\\{}", args[0]))
470 .replace(args[1], &format!("\\{}", args[1])),
471 args[1]
472 )
473 })
474}