1use std::collections::HashSet;
4
5use heck::{
6 ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase,
7 ToTrainCase, ToUpperCamelCase,
8};
9use regex::Regex;
10use serde_json::{Number, Value};
11
12use crate::functions::{Function, custom_error};
13use crate::interpreter::SearchResult;
14use crate::registry::register_if_enabled;
15use crate::{Context, Runtime, arg, defn};
16
17defn!(LowerFn, vec![arg!(string)], None);
22
23impl Function for LowerFn {
24 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
25 self.signature.validate(args, ctx)?;
26 let s = args[0]
27 .as_str()
28 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
29 Ok(Value::String(s.to_lowercase()))
30 }
31}
32
33defn!(UpperFn, vec![arg!(string)], None);
38
39impl Function for UpperFn {
40 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
41 self.signature.validate(args, ctx)?;
42 let s = args[0]
43 .as_str()
44 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
45 Ok(Value::String(s.to_uppercase()))
46 }
47}
48
49defn!(TrimFn, vec![arg!(string)], None);
54
55impl Function for TrimFn {
56 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
57 self.signature.validate(args, ctx)?;
58 let s = args[0]
59 .as_str()
60 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
61 Ok(Value::String(s.trim().to_string()))
62 }
63}
64
65defn!(TrimStartFn, vec![arg!(string)], None);
70
71impl Function for TrimStartFn {
72 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
73 self.signature.validate(args, ctx)?;
74 let s = args[0]
75 .as_str()
76 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
77 Ok(Value::String(s.trim_start().to_string()))
78 }
79}
80
81defn!(TrimEndFn, vec![arg!(string)], None);
86
87impl Function for TrimEndFn {
88 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
89 self.signature.validate(args, ctx)?;
90 let s = args[0]
91 .as_str()
92 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
93 Ok(Value::String(s.trim_end().to_string()))
94 }
95}
96
97defn!(SplitFn, vec![arg!(string), arg!(string)], None);
102
103impl Function for SplitFn {
104 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
105 self.signature.validate(args, ctx)?;
106 let s = args[0]
107 .as_str()
108 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
109 let delimiter = args[1]
110 .as_str()
111 .ok_or_else(|| custom_error(ctx, "Expected string delimiter"))?;
112
113 let parts: Vec<Value> = s
114 .split(delimiter)
115 .map(|part| Value::String(part.to_string()))
116 .collect();
117
118 Ok(Value::Array(parts))
119 }
120}
121
122defn!(
127 ReplaceFn,
128 vec![arg!(string), arg!(string), arg!(string)],
129 None
130);
131
132impl Function for ReplaceFn {
133 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
134 self.signature.validate(args, ctx)?;
135 let s = args[0]
136 .as_str()
137 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
138 let old = args[1]
139 .as_str()
140 .ok_or_else(|| custom_error(ctx, "Expected old string argument"))?;
141 let new = args[2]
142 .as_str()
143 .ok_or_else(|| custom_error(ctx, "Expected new string argument"))?;
144
145 Ok(Value::String(s.replace(old, new)))
146 }
147}
148
149defn!(
154 PadLeftFn,
155 vec![arg!(string), arg!(number), arg!(string)],
156 None
157);
158
159impl Function for PadLeftFn {
160 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
161 self.signature.validate(args, ctx)?;
162 let s = args[0]
163 .as_str()
164 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
165 let width = args[1]
166 .as_f64()
167 .map(|n| n as usize)
168 .ok_or_else(|| custom_error(ctx, "Expected positive number for width"))?;
169 let pad_char = args[2]
170 .as_str()
171 .ok_or_else(|| custom_error(ctx, "Expected string for pad character"))?;
172
173 let pad = pad_char.chars().next().unwrap_or(' ');
174 let result = if s.len() >= width {
175 s.to_string()
176 } else {
177 format!("{}{}", pad.to_string().repeat(width - s.len()), s)
178 };
179
180 Ok(Value::String(result))
181 }
182}
183
184defn!(
189 PadRightFn,
190 vec![arg!(string), arg!(number), arg!(string)],
191 None
192);
193
194impl Function for PadRightFn {
195 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
196 self.signature.validate(args, ctx)?;
197 let s = args[0]
198 .as_str()
199 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
200 let width = args[1]
201 .as_f64()
202 .map(|n| n as usize)
203 .ok_or_else(|| custom_error(ctx, "Expected positive number for width"))?;
204 let pad_char = args[2]
205 .as_str()
206 .ok_or_else(|| custom_error(ctx, "Expected string for pad character"))?;
207
208 let pad = pad_char.chars().next().unwrap_or(' ');
209 let result = if s.len() >= width {
210 s.to_string()
211 } else {
212 format!("{}{}", s, pad.to_string().repeat(width - s.len()))
213 };
214
215 Ok(Value::String(result))
216 }
217}
218
219defn!(
224 SubstrFn,
225 vec![arg!(string), arg!(number)],
226 Some(arg!(number))
227);
228
229impl Function for SubstrFn {
230 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
231 self.signature.validate(args, ctx)?;
232 let s = args[0]
233 .as_str()
234 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
235 let start = args[1]
236 .as_f64()
237 .map(|n| n as i64)
238 .ok_or_else(|| custom_error(ctx, "Expected number for start"))?;
239
240 let start_idx = if start < 0 {
242 (s.len() as i64 + start).max(0) as usize
243 } else {
244 start as usize
245 };
246
247 let result = if args.len() > 2 {
248 let length = args[2]
249 .as_f64()
250 .map(|n| n as usize)
251 .ok_or_else(|| custom_error(ctx, "Expected positive number for length"))?;
252 s.chars().skip(start_idx).take(length).collect()
253 } else {
254 s.chars().skip(start_idx).collect()
255 };
256
257 Ok(Value::String(result))
258 }
259}
260
261defn!(CapitalizeFn, vec![arg!(string)], None);
266
267impl Function for CapitalizeFn {
268 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
269 self.signature.validate(args, ctx)?;
270 let s = args[0]
271 .as_str()
272 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
273
274 let result = if s.is_empty() {
275 String::new()
276 } else {
277 let mut chars = s.chars();
278 match chars.next() {
279 None => String::new(),
280 Some(first) => first.to_uppercase().to_string() + chars.as_str(),
281 }
282 };
283
284 Ok(Value::String(result))
285 }
286}
287
288defn!(TitleFn, vec![arg!(string)], None);
293
294impl Function for TitleFn {
295 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
296 self.signature.validate(args, ctx)?;
297 let s = args[0]
298 .as_str()
299 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
300
301 let result = s
302 .split_whitespace()
303 .map(|word| {
304 let mut chars = word.chars();
305 match chars.next() {
306 None => String::new(),
307 Some(first) => {
308 first.to_uppercase().to_string() + &chars.as_str().to_lowercase()
309 }
310 }
311 })
312 .collect::<Vec<_>>()
313 .join(" ");
314
315 Ok(Value::String(result))
316 }
317}
318
319defn!(RepeatFn, vec![arg!(string), arg!(number)], None);
324
325impl Function for RepeatFn {
326 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
327 self.signature.validate(args, ctx)?;
328 let s = args[0]
329 .as_str()
330 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
331 let count = args[1]
332 .as_f64()
333 .map(|n| n as usize)
334 .ok_or_else(|| custom_error(ctx, "Expected positive number for count"))?;
335
336 Ok(Value::String(s.repeat(count)))
337 }
338}
339
340defn!(
345 IndexOfFn,
346 vec![arg!(string), arg!(string)],
347 Some(arg!(number))
348);
349
350fn normalize_index(idx: i64, len: usize) -> usize {
352 if idx < 0 {
353 (len as i64 + idx).max(0) as usize
354 } else {
355 (idx as usize).min(len)
356 }
357}
358
359impl Function for IndexOfFn {
360 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
361 self.signature.validate(args, ctx)?;
362 let s = args[0]
363 .as_str()
364 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
365 let search = args[1]
366 .as_str()
367 .ok_or_else(|| custom_error(ctx, "Expected search string"))?;
368
369 let len = s.len();
370
371 let start = if args.len() > 2 {
373 let start_val = args[2]
374 .as_f64()
375 .ok_or_else(|| custom_error(ctx, "Expected number for start"))?
376 as i64;
377 normalize_index(start_val, len)
378 } else {
379 0
380 };
381
382 let end = if args.len() > 3 {
384 let end_val = args[3]
385 .as_f64()
386 .ok_or_else(|| custom_error(ctx, "Expected number for end"))?
387 as i64;
388 normalize_index(end_val, len)
389 } else {
390 len
391 };
392
393 if start >= end || start >= len {
395 return Ok(Value::Null);
396 }
397
398 let slice = &s[start..end.min(len)];
399 match slice.find(search) {
400 Some(idx) => Ok(Value::Number(Number::from((start + idx) as i64))),
401 None => Ok(Value::Null),
402 }
403 }
404}
405
406defn!(
411 LastIndexOfFn,
412 vec![arg!(string), arg!(string)],
413 Some(arg!(number))
414);
415
416impl Function for LastIndexOfFn {
417 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
418 self.signature.validate(args, ctx)?;
419 let s = args[0]
420 .as_str()
421 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
422 let search = args[1]
423 .as_str()
424 .ok_or_else(|| custom_error(ctx, "Expected search string"))?;
425
426 let len = s.len();
427
428 let start = if args.len() > 2 {
430 let start_val = args[2]
431 .as_f64()
432 .ok_or_else(|| custom_error(ctx, "Expected number for start"))?
433 as i64;
434 normalize_index(start_val, len)
435 } else {
436 0
437 };
438
439 let end = if args.len() > 3 {
441 let end_val = args[3]
442 .as_f64()
443 .ok_or_else(|| custom_error(ctx, "Expected number for end"))?
444 as i64;
445 normalize_index(end_val, len)
446 } else {
447 len
448 };
449
450 if start >= end || start >= len {
452 return Ok(Value::Null);
453 }
454
455 let slice = &s[start..end.min(len)];
456 match slice.rfind(search) {
457 Some(idx) => Ok(Value::Number(Number::from((start + idx) as i64))),
458 None => Ok(Value::Null),
459 }
460 }
461}
462
463defn!(
468 SliceFn,
469 vec![arg!(string), arg!(number)],
470 Some(arg!(number))
471);
472
473impl Function for SliceFn {
474 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
475 self.signature.validate(args, ctx)?;
476 let s = args[0]
477 .as_str()
478 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
479
480 let len = s.len() as i64;
481
482 let start = args[1]
483 .as_f64()
484 .map(|n| n as i64)
485 .ok_or_else(|| custom_error(ctx, "Expected number for start"))?;
486
487 let start_idx = if start < 0 {
489 (len + start).max(0) as usize
490 } else {
491 start.min(len) as usize
492 };
493
494 let end_idx = if args.len() > 2 {
495 let end = args[2]
496 .as_f64()
497 .map(|n| n as i64)
498 .ok_or_else(|| custom_error(ctx, "Expected number for end"))?;
499 if end < 0 {
500 (len + end).max(0) as usize
501 } else {
502 end.min(len) as usize
503 }
504 } else {
505 len as usize
506 };
507
508 let result: String = s
509 .chars()
510 .skip(start_idx)
511 .take(end_idx.saturating_sub(start_idx))
512 .collect();
513
514 Ok(Value::String(result))
515 }
516}
517
518defn!(ConcatFn, vec![arg!(array)], Some(arg!(string)));
523
524impl Function for ConcatFn {
525 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
526 self.signature.validate(args, ctx)?;
527 let arr = args[0]
528 .as_array()
529 .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
530
531 let separator = if args.len() > 1 {
532 args[1].as_str().map(|s| s.to_string()).unwrap_or_default()
533 } else {
534 String::new()
535 };
536
537 let strings: Vec<String> = arr
538 .iter()
539 .filter_map(|v| v.as_str().map(|s| s.to_string()))
540 .collect();
541
542 Ok(Value::String(strings.join(&separator)))
543 }
544}
545
546defn!(UpperCaseFn, vec![arg!(string)], None);
551
552impl Function for UpperCaseFn {
553 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
554 self.signature.validate(args, ctx)?;
555 let s = args[0]
556 .as_str()
557 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
558 Ok(Value::String(s.to_uppercase()))
559 }
560}
561
562defn!(LowerCaseFn, vec![arg!(string)], None);
567
568impl Function for LowerCaseFn {
569 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
570 self.signature.validate(args, ctx)?;
571 let s = args[0]
572 .as_str()
573 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
574 Ok(Value::String(s.to_lowercase()))
575 }
576}
577
578defn!(TitleCaseFn, vec![arg!(string)], None);
584
585impl Function for TitleCaseFn {
586 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
587 self.signature.validate(args, ctx)?;
588 let s = args[0]
589 .as_str()
590 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
591 Ok(Value::String(s.to_title_case()))
592 }
593}
594
595defn!(CamelCaseFn, vec![arg!(string)], None);
601
602impl Function for CamelCaseFn {
603 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
604 self.signature.validate(args, ctx)?;
605 let s = args[0]
606 .as_str()
607 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
608 Ok(Value::String(s.to_lower_camel_case()))
609 }
610}
611
612defn!(SnakeCaseFn, vec![arg!(string)], None);
618
619impl Function for SnakeCaseFn {
620 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
621 self.signature.validate(args, ctx)?;
622 let s = args[0]
623 .as_str()
624 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
625 Ok(Value::String(s.to_snake_case()))
626 }
627}
628
629defn!(KebabCaseFn, vec![arg!(string)], None);
635
636impl Function for KebabCaseFn {
637 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
638 self.signature.validate(args, ctx)?;
639 let s = args[0]
640 .as_str()
641 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
642 Ok(Value::String(s.to_kebab_case()))
643 }
644}
645
646defn!(PascalCaseFn, vec![arg!(string)], None);
652
653impl Function for PascalCaseFn {
654 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
655 self.signature.validate(args, ctx)?;
656 let s = args[0]
657 .as_str()
658 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
659 Ok(Value::String(s.to_upper_camel_case()))
660 }
661}
662
663defn!(ShoutySnakeCaseFn, vec![arg!(string)], None);
669
670impl Function for ShoutySnakeCaseFn {
671 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
672 self.signature.validate(args, ctx)?;
673 let s = args[0]
674 .as_str()
675 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
676 Ok(Value::String(s.to_shouty_snake_case()))
677 }
678}
679
680defn!(ShoutyKebabCaseFn, vec![arg!(string)], None);
686
687impl Function for ShoutyKebabCaseFn {
688 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
689 self.signature.validate(args, ctx)?;
690 let s = args[0]
691 .as_str()
692 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
693 Ok(Value::String(s.to_shouty_kebab_case()))
694 }
695}
696
697defn!(TrainCaseFn, vec![arg!(string)], None);
703
704impl Function for TrainCaseFn {
705 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
706 self.signature.validate(args, ctx)?;
707 let s = args[0]
708 .as_str()
709 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
710 Ok(Value::String(s.to_train_case()))
711 }
712}
713
714defn!(
719 TruncateFn,
720 vec![arg!(string), arg!(number)],
721 Some(arg!(string))
722);
723
724impl Function for TruncateFn {
725 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
726 self.signature.validate(args, ctx)?;
727 let s = args[0]
728 .as_str()
729 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
730 let max_len = args[1]
731 .as_f64()
732 .ok_or_else(|| custom_error(ctx, "Expected number for length"))?
733 as usize;
734
735 let suffix = args
736 .get(2)
737 .and_then(|v| v.as_str())
738 .map(|s| s.to_string())
739 .unwrap_or_else(|| "...".to_string());
740
741 if s.len() <= max_len {
742 Ok(Value::String(s.to_string()))
743 } else {
744 let truncate_at = max_len.saturating_sub(suffix.len());
745 let truncated: String = s.chars().take(truncate_at).collect();
746 Ok(Value::String(format!("{}{}", truncated, suffix)))
747 }
748 }
749}
750
751defn!(WrapFn, vec![arg!(string), arg!(number)], None);
756
757impl Function for WrapFn {
758 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
759 self.signature.validate(args, ctx)?;
760 let s = args[0]
761 .as_str()
762 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
763 let width = args[1]
764 .as_f64()
765 .ok_or_else(|| custom_error(ctx, "Expected number for width"))?
766 as usize;
767
768 if width == 0 {
769 return Ok(Value::String(s.to_string()));
770 }
771
772 let mut lines: Vec<String> = Vec::new();
773
774 for paragraph in s.split('\n') {
776 let mut current_line = String::new();
777
778 for word in paragraph.split_whitespace() {
779 if current_line.is_empty() {
780 current_line = word.to_string();
781 } else if current_line.len() + 1 + word.len() <= width {
782 current_line.push(' ');
783 current_line.push_str(word);
784 } else {
785 lines.push(current_line);
786 current_line = word.to_string();
787 }
788 }
789
790 lines.push(current_line);
792 }
793
794 if !s.ends_with('\n') && lines.last().is_some_and(|l| l.is_empty()) {
796 lines.pop();
797 }
798
799 if lines.is_empty() && !s.is_empty() {
801 return Ok(Value::String(s.to_string()));
802 }
803
804 Ok(Value::String(lines.join("\n")))
806 }
807}
808
809defn!(FormatFn, vec![arg!(string)], Some(arg!(any)));
818
819fn var_to_format_string(v: &Value) -> String {
821 match v {
822 Value::String(s) => s.clone(),
823 Value::Number(n) => n.to_string(),
824 Value::Bool(b) => b.to_string(),
825 Value::Null => "null".to_string(),
826 _ => serde_json::to_string(v).unwrap_or_else(|_| "null".to_string()),
827 }
828}
829
830impl Function for FormatFn {
831 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
832 self.signature.validate(args, ctx)?;
833 let template = args[0]
834 .as_str()
835 .ok_or_else(|| custom_error(ctx, "Expected template string"))?;
836
837 let mut result = template.to_string();
838
839 if args.len() == 2 {
841 if let Some(arr) = args[1].as_array() {
842 for (i, item) in arr.iter().enumerate() {
844 let placeholder = format!("{{{}}}", i);
845 let value = var_to_format_string(item);
846 result = result.replace(&placeholder, &value);
847 }
848 return Ok(Value::String(result));
849 } else if let Some(obj) = args[1].as_object() {
850 for (key, val) in obj.iter() {
852 let placeholder = format!("{{{}}}", key);
853 let value = var_to_format_string(val);
854 result = result.replace(&placeholder, &value);
855 }
856 return Ok(Value::String(result));
857 }
858 }
859
860 for (i, arg) in args.iter().skip(1).enumerate() {
862 let placeholder = format!("{{{}}}", i);
863 let value = var_to_format_string(arg);
864 result = result.replace(&placeholder, &value);
865 }
866
867 Ok(Value::String(result))
868 }
869}
870
871defn!(SprintfFn, vec![arg!(string)], Some(arg!(any)));
877
878impl Function for SprintfFn {
879 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
880 self.signature.validate(args, ctx)?;
881 let format_str = args[0]
882 .as_str()
883 .ok_or_else(|| custom_error(ctx, "Expected format string"))?;
884
885 let format_args: Vec<&Value> = if args.len() == 2 {
887 if let Some(arr) = args[1].as_array() {
888 arr.iter().collect()
889 } else {
890 args.iter().skip(1).collect()
891 }
892 } else {
893 args.iter().skip(1).collect()
894 };
895
896 let mut result = String::new();
897 let mut arg_index = 0;
898 let mut chars = format_str.chars().peekable();
899
900 while let Some(c) = chars.next() {
901 if c == '%' {
902 if let Some(&next) = chars.peek() {
903 if next == '%' {
904 result.push('%');
906 chars.next();
907 continue;
908 }
909
910 let mut width = String::new();
912 let mut precision = String::new();
913 let mut in_precision = false;
914
915 while let Some(&ch) = chars.peek() {
917 if ch == '.' {
918 in_precision = true;
919 chars.next();
920 } else if ch.is_ascii_digit() || ch == '-' || ch == '+' {
921 if in_precision {
922 precision.push(ch);
923 } else {
924 width.push(ch);
925 }
926 chars.next();
927 } else {
928 break;
929 }
930 }
931
932 if let Some(fmt_type) = chars.next() {
934 if arg_index < format_args.len() {
935 let arg = format_args[arg_index];
936 arg_index += 1;
937
938 let formatted = match fmt_type {
939 's' => var_to_format_string(arg),
940 'd' | 'i' => {
941 if let Some(n) = arg.as_f64() {
942 format!("{}", n as i64)
943 } else {
944 "0".to_string()
945 }
946 }
947 'f' => {
948 if let Some(n) = arg.as_f64() {
949 let prec: usize = precision.parse().unwrap_or(6);
950 format!("{:.prec$}", n, prec = prec)
951 } else {
952 "0.0".to_string()
953 }
954 }
955 'e' => {
956 if let Some(n) = arg.as_f64() {
957 let prec: usize = precision.parse().unwrap_or(6);
958 format!("{:.prec$e}", n, prec = prec)
959 } else {
960 "0e0".to_string()
961 }
962 }
963 'x' => {
964 if let Some(n) = arg.as_f64() {
965 format!("{:x}", n as i64)
966 } else {
967 "0".to_string()
968 }
969 }
970 'X' => {
971 if let Some(n) = arg.as_f64() {
972 format!("{:X}", n as i64)
973 } else {
974 "0".to_string()
975 }
976 }
977 'o' => {
978 if let Some(n) = arg.as_f64() {
979 format!("{:o}", n as i64)
980 } else {
981 "0".to_string()
982 }
983 }
984 'b' => {
985 if let Some(n) = arg.as_f64() {
986 format!("{:b}", n as i64)
987 } else {
988 "0".to_string()
989 }
990 }
991 'c' => {
992 if let Some(n) = arg.as_f64() {
993 char::from_u32(n as u32)
994 .map(|c| c.to_string())
995 .unwrap_or_default()
996 } else if let Some(s) = arg.as_str() {
997 s.chars().next().map(|c| c.to_string()).unwrap_or_default()
998 } else {
999 String::new()
1000 }
1001 }
1002 _ => {
1003 format!("%{}{}", width, fmt_type)
1005 }
1006 };
1007
1008 if !width.is_empty() {
1010 let w: i32 = width.parse().unwrap_or(0);
1011 if w < 0 {
1012 result.push_str(&format!(
1014 "{:<width$}",
1015 formatted,
1016 width = w.unsigned_abs() as usize
1017 ));
1018 } else {
1019 result.push_str(&format!(
1021 "{:>width$}",
1022 formatted,
1023 width = w as usize
1024 ));
1025 }
1026 } else {
1027 result.push_str(&formatted);
1028 }
1029 } else {
1030 result.push('%');
1032 result.push_str(&width);
1033 if !precision.is_empty() {
1034 result.push('.');
1035 result.push_str(&precision);
1036 }
1037 result.push(fmt_type);
1038 }
1039 }
1040 } else {
1041 result.push('%');
1043 }
1044 } else {
1045 result.push(c);
1046 }
1047 }
1048
1049 Ok(Value::String(result))
1050 }
1051}
1052
1053defn!(LtrimstrFn, vec![arg!(string), arg!(string)], None);
1058
1059impl Function for LtrimstrFn {
1060 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1061 self.signature.validate(args, ctx)?;
1062 let s = args[0]
1063 .as_str()
1064 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1065 let prefix = args[1]
1066 .as_str()
1067 .ok_or_else(|| custom_error(ctx, "Expected prefix string"))?;
1068
1069 let result = s.strip_prefix(prefix).unwrap_or(s).to_string();
1070 Ok(Value::String(result))
1071 }
1072}
1073
1074defn!(RtrimstrFn, vec![arg!(string), arg!(string)], None);
1079
1080impl Function for RtrimstrFn {
1081 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1082 self.signature.validate(args, ctx)?;
1083 let s = args[0]
1084 .as_str()
1085 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1086 let suffix = args[1]
1087 .as_str()
1088 .ok_or_else(|| custom_error(ctx, "Expected suffix string"))?;
1089
1090 let result = s.strip_suffix(suffix).unwrap_or(s).to_string();
1091 Ok(Value::String(result))
1092 }
1093}
1094
1095defn!(IndicesFn, vec![arg!(string), arg!(string)], None);
1100
1101impl Function for IndicesFn {
1102 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1103 self.signature.validate(args, ctx)?;
1104 let s = args[0]
1105 .as_str()
1106 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1107 let search = args[1]
1108 .as_str()
1109 .ok_or_else(|| custom_error(ctx, "Expected search string"))?;
1110
1111 let mut indices: Vec<Value> = Vec::new();
1113 if !search.is_empty() {
1114 let mut start = 0;
1115 while let Some(pos) = s[start..].find(search) {
1116 let actual_pos = start + pos;
1117 indices.push(Value::Number(Number::from(actual_pos as i64)));
1118 start = actual_pos + 1; }
1120 }
1121
1122 Ok(Value::Array(indices))
1123 }
1124}
1125
1126defn!(InsideFn, vec![arg!(string), arg!(string)], None);
1131
1132impl Function for InsideFn {
1133 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1134 self.signature.validate(args, ctx)?;
1135 let search = args[0]
1136 .as_str()
1137 .ok_or_else(|| custom_error(ctx, "Expected search string"))?;
1138 let s = args[1]
1139 .as_str()
1140 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1141
1142 Ok(Value::Bool(s.contains(search)))
1143 }
1144}
1145
1146defn!(HumanizeFn, vec![arg!(string)], None);
1152
1153impl Function for HumanizeFn {
1154 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1155 self.signature.validate(args, ctx)?;
1156 let s = args[0]
1157 .as_str()
1158 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1159
1160 let mut result = String::new();
1162 let mut prev_was_lower = false;
1163 let mut word_start = true;
1164
1165 for c in s.chars() {
1166 if c == '_' || c == '-' {
1167 if !result.is_empty() && !result.ends_with(' ') {
1168 result.push(' ');
1169 }
1170 word_start = true;
1171 prev_was_lower = false;
1172 } else if c.is_uppercase() && prev_was_lower {
1173 result.push(' ');
1175 if word_start {
1176 result.push(c); } else {
1178 result.push(c.to_lowercase().next().unwrap_or(c));
1179 }
1180 word_start = false;
1181 prev_was_lower = false;
1182 } else {
1183 if word_start && result.is_empty() {
1184 result.push(c.to_uppercase().next().unwrap_or(c));
1186 } else {
1187 result.push(c.to_lowercase().next().unwrap_or(c));
1188 }
1189 prev_was_lower = c.is_lowercase();
1190 word_start = false;
1191 }
1192 }
1193
1194 Ok(Value::String(result))
1195 }
1196}
1197
1198defn!(DeburrrFn, vec![arg!(string)], None);
1204
1205impl Function for DeburrrFn {
1206 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1207 self.signature.validate(args, ctx)?;
1208 let s = args[0]
1209 .as_str()
1210 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1211
1212 let result: String = s
1214 .chars()
1215 .map(|c| match c {
1216 '\u{00C0}' | '\u{00C1}' | '\u{00C2}' | '\u{00C3}' | '\u{00C4}' | '\u{00C5}' => 'A',
1217 '\u{00C6}' => 'A', '\u{00C7}' => 'C',
1219 '\u{00C8}' | '\u{00C9}' | '\u{00CA}' | '\u{00CB}' => 'E',
1220 '\u{00CC}' | '\u{00CD}' | '\u{00CE}' | '\u{00CF}' => 'I',
1221 '\u{00D0}' => 'D',
1222 '\u{00D1}' => 'N',
1223 '\u{00D2}' | '\u{00D3}' | '\u{00D4}' | '\u{00D5}' | '\u{00D6}' | '\u{00D8}' => 'O',
1224 '\u{00D9}' | '\u{00DA}' | '\u{00DB}' | '\u{00DC}' => 'U',
1225 '\u{00DD}' => 'Y',
1226 '\u{00DE}' => 'T', '\u{00DF}' => 's', '\u{00E0}' | '\u{00E1}' | '\u{00E2}' | '\u{00E3}' | '\u{00E4}' | '\u{00E5}' => 'a',
1229 '\u{00E6}' => 'a', '\u{00E7}' => 'c',
1231 '\u{00E8}' | '\u{00E9}' | '\u{00EA}' | '\u{00EB}' => 'e',
1232 '\u{00EC}' | '\u{00ED}' | '\u{00EE}' | '\u{00EF}' => 'i',
1233 '\u{00F0}' => 'd',
1234 '\u{00F1}' => 'n',
1235 '\u{00F2}' | '\u{00F3}' | '\u{00F4}' | '\u{00F5}' | '\u{00F6}' | '\u{00F8}' => 'o',
1236 '\u{00F9}' | '\u{00FA}' | '\u{00FB}' | '\u{00FC}' => 'u',
1237 '\u{00FD}' | '\u{00FF}' => 'y',
1238 '\u{00FE}' => 't', _ => c,
1240 })
1241 .collect();
1242
1243 Ok(Value::String(result))
1244 }
1245}
1246
1247defn!(WordsFn, vec![arg!(string)], None);
1253
1254impl Function for WordsFn {
1255 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1256 self.signature.validate(args, ctx)?;
1257 let s = args[0]
1258 .as_str()
1259 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1260
1261 let mut words = Vec::new();
1263 let mut current_word = String::new();
1264 let mut prev_was_lower = false;
1265
1266 for c in s.chars() {
1267 if c.is_whitespace() || c == '_' || c == '-' {
1268 if !current_word.is_empty() {
1269 words.push(Value::String(current_word.clone()));
1270 current_word.clear();
1271 }
1272 prev_was_lower = false;
1273 } else if c.is_uppercase() && prev_was_lower {
1274 if !current_word.is_empty() {
1276 words.push(Value::String(current_word.clone()));
1277 current_word.clear();
1278 }
1279 current_word.push(c);
1280 prev_was_lower = false;
1281 } else {
1282 current_word.push(c);
1283 prev_was_lower = c.is_lowercase();
1284 }
1285 }
1286
1287 if !current_word.is_empty() {
1288 words.push(Value::String(current_word));
1289 }
1290
1291 Ok(Value::Array(words))
1292 }
1293}
1294
1295defn!(EscapeFn, vec![arg!(string)], None);
1301
1302impl Function for EscapeFn {
1303 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1304 self.signature.validate(args, ctx)?;
1305 let s = args[0]
1306 .as_str()
1307 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1308
1309 let result = s
1310 .replace('&', "&")
1311 .replace('<', "<")
1312 .replace('>', ">")
1313 .replace('"', """)
1314 .replace('\'', "'");
1315
1316 Ok(Value::String(result))
1317 }
1318}
1319
1320defn!(UnescapeFn, vec![arg!(string)], None);
1326
1327impl Function for UnescapeFn {
1328 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1329 self.signature.validate(args, ctx)?;
1330 let s = args[0]
1331 .as_str()
1332 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1333
1334 let result = s
1335 .replace("&", "&")
1336 .replace("<", "<")
1337 .replace(">", ">")
1338 .replace(""", "\"")
1339 .replace("'", "'");
1340
1341 Ok(Value::String(result))
1342 }
1343}
1344
1345defn!(EscapeRegexFn, vec![arg!(string)], None);
1351
1352impl Function for EscapeRegexFn {
1353 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1354 self.signature.validate(args, ctx)?;
1355 let s = args[0]
1356 .as_str()
1357 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1358
1359 let mut result = String::with_capacity(s.len() * 2);
1361 for c in s.chars() {
1362 match c {
1363 '\\' | '^' | '$' | '.' | '|' | '?' | '*' | '+' | '(' | ')' | '[' | ']' | '{'
1364 | '}' => {
1365 result.push('\\');
1366 result.push(c);
1367 }
1368 _ => result.push(c),
1369 }
1370 }
1371
1372 Ok(Value::String(result))
1373 }
1374}
1375
1376defn!(StartCaseFn, vec![arg!(string)], None);
1382
1383impl Function for StartCaseFn {
1384 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1385 self.signature.validate(args, ctx)?;
1386 let s = args[0]
1387 .as_str()
1388 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1389
1390 let mut result = String::new();
1392 let mut prev_was_lower = false;
1393 let mut word_start = true;
1394
1395 for c in s.chars() {
1396 if c.is_whitespace() || c == '_' || c == '-' {
1397 if !result.is_empty() && !result.ends_with(' ') {
1398 result.push(' ');
1399 }
1400 word_start = true;
1401 prev_was_lower = false;
1402 } else if c.is_uppercase() && prev_was_lower {
1403 result.push(' ');
1405 result.push(c); word_start = false;
1407 prev_was_lower = false;
1408 } else {
1409 if word_start {
1410 result.push(c.to_uppercase().next().unwrap_or(c));
1411 } else {
1412 result.push(c.to_lowercase().next().unwrap_or(c));
1413 }
1414 prev_was_lower = c.is_lowercase();
1415 word_start = false;
1416 }
1417 }
1418
1419 Ok(Value::String(result))
1420 }
1421}
1422
1423defn!(MaskFn, vec![arg!(string)], Some(arg!(any)));
1429
1430impl Function for MaskFn {
1431 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1432 self.signature.validate(args, ctx)?;
1433 let s = args[0]
1434 .as_str()
1435 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1436
1437 let visible = if args.len() > 1 && !args[1].is_null() {
1439 args[1].as_f64().unwrap_or(0.0) as usize
1440 } else {
1441 0
1442 };
1443
1444 let mask_char = if args.len() > 2 && !args[2].is_null() {
1446 args[2]
1447 .as_str()
1448 .and_then(|s| s.chars().next())
1449 .unwrap_or('*')
1450 } else {
1451 '*'
1452 };
1453
1454 let char_count = s.chars().count();
1455
1456 if visible >= char_count {
1457 return Ok(Value::String(s.to_string()));
1459 }
1460
1461 let mask_count = char_count - visible;
1462 let masked: String = std::iter::repeat_n(mask_char, mask_count)
1463 .chain(s.chars().skip(mask_count))
1464 .collect();
1465
1466 Ok(Value::String(masked))
1467 }
1468}
1469
1470defn!(RedactFn, vec![arg!(string), arg!(string)], Some(arg!(any)));
1476
1477impl Function for RedactFn {
1478 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1479 self.signature.validate(args, ctx)?;
1480 let s = args[0]
1481 .as_str()
1482 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1483 let pattern = args[1]
1484 .as_str()
1485 .ok_or_else(|| custom_error(ctx, "Expected string pattern"))?;
1486
1487 let replacement = if args.len() > 2 && !args[2].is_null() {
1489 args[2]
1490 .as_str()
1491 .map(|s| s.to_string())
1492 .unwrap_or_else(|| "[REDACTED]".to_string())
1493 } else {
1494 "[REDACTED]".to_string()
1495 };
1496
1497 let re = Regex::new(pattern)
1498 .map_err(|e| custom_error(ctx, &format!("Invalid regex pattern: {}", e)))?;
1499
1500 let result = re.replace_all(s, replacement.as_str());
1501 Ok(Value::String(result.into_owned()))
1502 }
1503}
1504
1505defn!(NormalizeWhitespaceFn, vec![arg!(string)], None);
1511
1512impl Function for NormalizeWhitespaceFn {
1513 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1514 self.signature.validate(args, ctx)?;
1515 let s = args[0]
1516 .as_str()
1517 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1518
1519 let result: String = s.split_whitespace().collect::<Vec<_>>().join(" ");
1521
1522 Ok(Value::String(result))
1523 }
1524}
1525
1526defn!(IsBlankFn, vec![arg!(string)], None);
1532
1533impl Function for IsBlankFn {
1534 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1535 self.signature.validate(args, ctx)?;
1536 let s = args[0]
1537 .as_str()
1538 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1539
1540 let is_blank = s.trim().is_empty();
1541 Ok(Value::Bool(is_blank))
1542 }
1543}
1544
1545defn!(
1551 AbbreviateFn,
1552 vec![arg!(string), arg!(number)],
1553 Some(arg!(any))
1554);
1555
1556impl Function for AbbreviateFn {
1557 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1558 self.signature.validate(args, ctx)?;
1559 let s = args[0]
1560 .as_str()
1561 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1562 let max_length = args[1]
1563 .as_f64()
1564 .ok_or_else(|| custom_error(ctx, "Expected number for max_length"))?
1565 as usize;
1566
1567 let suffix = if args.len() > 2 && !args[2].is_null() {
1569 args[2]
1570 .as_str()
1571 .map(|s| s.to_string())
1572 .unwrap_or_else(|| "...".to_string())
1573 } else {
1574 "...".to_string()
1575 };
1576
1577 let char_count = s.chars().count();
1578 let suffix_len = suffix.chars().count();
1579
1580 if char_count <= max_length {
1581 return Ok(Value::String(s.to_string()));
1582 }
1583
1584 if max_length <= suffix_len {
1585 let result: String = s.chars().take(max_length).collect();
1587 return Ok(Value::String(result));
1588 }
1589
1590 let truncate_at = max_length - suffix_len;
1591 let mut result: String = s.chars().take(truncate_at).collect();
1592 result.push_str(&suffix);
1593
1594 Ok(Value::String(result))
1595 }
1596}
1597
1598defn!(CenterFn, vec![arg!(string), arg!(number)], Some(arg!(any)));
1604
1605impl Function for CenterFn {
1606 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1607 self.signature.validate(args, ctx)?;
1608 let s = args[0]
1609 .as_str()
1610 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1611 let width = args[1]
1612 .as_f64()
1613 .ok_or_else(|| custom_error(ctx, "Expected number for width"))?
1614 as usize;
1615
1616 let pad_char = if args.len() > 2 && !args[2].is_null() {
1618 args[2]
1619 .as_str()
1620 .and_then(|s| s.chars().next())
1621 .unwrap_or(' ')
1622 } else {
1623 ' '
1624 };
1625
1626 let char_count = s.chars().count();
1627
1628 if char_count >= width {
1629 return Ok(Value::String(s.to_string()));
1630 }
1631
1632 let total_padding = width - char_count;
1633 let left_padding = total_padding / 2;
1634 let right_padding = total_padding - left_padding;
1635
1636 let mut result = String::with_capacity(width);
1637 for _ in 0..left_padding {
1638 result.push(pad_char);
1639 }
1640 result.push_str(s);
1641 for _ in 0..right_padding {
1642 result.push(pad_char);
1643 }
1644
1645 Ok(Value::String(result))
1646 }
1647}
1648
1649defn!(ReverseStringFn, vec![arg!(string)], None);
1655
1656impl Function for ReverseStringFn {
1657 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1658 self.signature.validate(args, ctx)?;
1659 let s = args[0]
1660 .as_str()
1661 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1662
1663 let result: String = s.chars().rev().collect();
1664 Ok(Value::String(result))
1665 }
1666}
1667
1668defn!(ExplodeFn, vec![arg!(string)], None);
1674
1675impl Function for ExplodeFn {
1676 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1677 self.signature.validate(args, ctx)?;
1678 let s = args[0]
1679 .as_str()
1680 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1681
1682 let codepoints: Vec<Value> = s
1683 .chars()
1684 .map(|c| Value::Number(Number::from(c as u32)))
1685 .collect();
1686
1687 Ok(Value::Array(codepoints))
1688 }
1689}
1690
1691defn!(ImplodeFn, vec![arg!(array)], None);
1697
1698impl Function for ImplodeFn {
1699 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1700 self.signature.validate(args, ctx)?;
1701 let arr = args[0]
1702 .as_array()
1703 .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1704
1705 let mut result = String::new();
1706 for item in arr.iter() {
1707 let codepoint = item
1708 .as_f64()
1709 .ok_or_else(|| custom_error(ctx, "Expected array of numbers (codepoints)"))?
1710 as u32;
1711
1712 let c = char::from_u32(codepoint).ok_or_else(|| {
1713 custom_error(ctx, &format!("Invalid Unicode codepoint: {}", codepoint))
1714 })?;
1715 result.push(c);
1716 }
1717
1718 Ok(Value::String(result))
1719 }
1720}
1721
1722defn!(ShellEscapeFn, vec![arg!(string)], None);
1728
1729impl Function for ShellEscapeFn {
1730 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1731 self.signature.validate(args, ctx)?;
1732 let s = args[0]
1733 .as_str()
1734 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1735
1736 let escaped = if s.is_empty() {
1740 "''".to_string()
1741 } else if s
1742 .chars()
1743 .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.' || c == '/')
1744 {
1745 s.to_string()
1747 } else if !s.contains('\'') {
1748 format!("'{}'", s)
1750 } else {
1751 let mut result = String::with_capacity(s.len() + 10);
1753 result.push('\'');
1754 for c in s.chars() {
1755 if c == '\'' {
1756 result.push_str("'\\''");
1757 } else {
1758 result.push(c);
1759 }
1760 }
1761 result.push('\'');
1762 result
1763 };
1764
1765 Ok(Value::String(escaped))
1766 }
1767}
1768
1769pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
1775 register_if_enabled(runtime, "lower", enabled, Box::new(LowerFn::new()));
1776 register_if_enabled(runtime, "upper", enabled, Box::new(UpperFn::new()));
1777 register_if_enabled(runtime, "trim", enabled, Box::new(TrimFn::new()));
1778 register_if_enabled(runtime, "trim_left", enabled, Box::new(TrimStartFn::new()));
1779 register_if_enabled(runtime, "trim_right", enabled, Box::new(TrimEndFn::new()));
1780 register_if_enabled(runtime, "split", enabled, Box::new(SplitFn::new()));
1781 register_if_enabled(runtime, "replace", enabled, Box::new(ReplaceFn::new()));
1782 register_if_enabled(runtime, "pad_left", enabled, Box::new(PadLeftFn::new()));
1783 register_if_enabled(runtime, "pad_right", enabled, Box::new(PadRightFn::new()));
1784 register_if_enabled(runtime, "substr", enabled, Box::new(SubstrFn::new()));
1785 register_if_enabled(
1786 runtime,
1787 "capitalize",
1788 enabled,
1789 Box::new(CapitalizeFn::new()),
1790 );
1791 register_if_enabled(runtime, "title", enabled, Box::new(TitleFn::new()));
1792 register_if_enabled(runtime, "repeat", enabled, Box::new(RepeatFn::new()));
1793 register_if_enabled(runtime, "find_first", enabled, Box::new(IndexOfFn::new()));
1794 register_if_enabled(
1795 runtime,
1796 "find_last",
1797 enabled,
1798 Box::new(LastIndexOfFn::new()),
1799 );
1800 register_if_enabled(runtime, "slice", enabled, Box::new(SliceFn::new()));
1801 register_if_enabled(runtime, "concat", enabled, Box::new(ConcatFn::new()));
1802 register_if_enabled(runtime, "upper_case", enabled, Box::new(UpperCaseFn::new()));
1803 register_if_enabled(runtime, "lower_case", enabled, Box::new(LowerCaseFn::new()));
1804 register_if_enabled(runtime, "title_case", enabled, Box::new(TitleCaseFn::new()));
1805 register_if_enabled(runtime, "camel_case", enabled, Box::new(CamelCaseFn::new()));
1806 register_if_enabled(runtime, "snake_case", enabled, Box::new(SnakeCaseFn::new()));
1807 register_if_enabled(runtime, "kebab_case", enabled, Box::new(KebabCaseFn::new()));
1808 register_if_enabled(
1809 runtime,
1810 "pascal_case",
1811 enabled,
1812 Box::new(PascalCaseFn::new()),
1813 );
1814 register_if_enabled(
1815 runtime,
1816 "shouty_snake_case",
1817 enabled,
1818 Box::new(ShoutySnakeCaseFn::new()),
1819 );
1820 register_if_enabled(
1821 runtime,
1822 "shouty_kebab_case",
1823 enabled,
1824 Box::new(ShoutyKebabCaseFn::new()),
1825 );
1826 register_if_enabled(runtime, "train_case", enabled, Box::new(TrainCaseFn::new()));
1827 register_if_enabled(runtime, "truncate", enabled, Box::new(TruncateFn::new()));
1828 register_if_enabled(runtime, "wrap", enabled, Box::new(WrapFn::new()));
1829 register_if_enabled(runtime, "format", enabled, Box::new(FormatFn::new()));
1830 register_if_enabled(runtime, "sprintf", enabled, Box::new(SprintfFn::new()));
1831 register_if_enabled(runtime, "ltrimstr", enabled, Box::new(LtrimstrFn::new()));
1832 register_if_enabled(runtime, "rtrimstr", enabled, Box::new(RtrimstrFn::new()));
1833 register_if_enabled(runtime, "indices", enabled, Box::new(IndicesFn::new()));
1834 register_if_enabled(runtime, "inside", enabled, Box::new(InsideFn::new()));
1835 register_if_enabled(runtime, "humanize", enabled, Box::new(HumanizeFn::new()));
1836 register_if_enabled(runtime, "deburr", enabled, Box::new(DeburrrFn::new()));
1837 register_if_enabled(runtime, "words", enabled, Box::new(WordsFn::new()));
1838 register_if_enabled(runtime, "escape", enabled, Box::new(EscapeFn::new()));
1839 register_if_enabled(runtime, "unescape", enabled, Box::new(UnescapeFn::new()));
1840 register_if_enabled(
1841 runtime,
1842 "escape_regex",
1843 enabled,
1844 Box::new(EscapeRegexFn::new()),
1845 );
1846 register_if_enabled(runtime, "start_case", enabled, Box::new(StartCaseFn::new()));
1847 register_if_enabled(runtime, "mask", enabled, Box::new(MaskFn::new()));
1848 register_if_enabled(runtime, "redact", enabled, Box::new(RedactFn::new()));
1849 register_if_enabled(
1850 runtime,
1851 "normalize_whitespace",
1852 enabled,
1853 Box::new(NormalizeWhitespaceFn::new()),
1854 );
1855 register_if_enabled(runtime, "is_blank", enabled, Box::new(IsBlankFn::new()));
1856 register_if_enabled(
1857 runtime,
1858 "abbreviate",
1859 enabled,
1860 Box::new(AbbreviateFn::new()),
1861 );
1862 register_if_enabled(runtime, "center", enabled, Box::new(CenterFn::new()));
1863 register_if_enabled(
1864 runtime,
1865 "reverse_string",
1866 enabled,
1867 Box::new(ReverseStringFn::new()),
1868 );
1869 register_if_enabled(runtime, "explode", enabled, Box::new(ExplodeFn::new()));
1870 register_if_enabled(runtime, "implode", enabled, Box::new(ImplodeFn::new()));
1871 register_if_enabled(
1872 runtime,
1873 "shell_escape",
1874 enabled,
1875 Box::new(ShellEscapeFn::new()),
1876 );
1877}