robinpath_modules/modules/
regex_mod.rs1use regex::Regex;
2use robinpath::{RobinPath, Value};
3
4pub fn register(rp: &mut RobinPath) {
5 rp.register_builtin("regex.test", |args, _| {
6 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
7 let pattern = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
8 let flags = args.get(2).map(|v| v.to_display_string()).unwrap_or_default();
9 let re = build_regex(&pattern, &flags)?;
10 Ok(Value::Bool(re.is_match(&s)))
11 });
12
13 rp.register_builtin("regex.match", |args, _| {
14 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
15 let pattern = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
16 let flags = args.get(2).map(|v| v.to_display_string()).unwrap_or_default();
17 let re = build_regex(&pattern, &flags)?;
18 match re.find(&s) {
19 Some(m) => Ok(Value::String(m.as_str().to_string())),
20 None => Ok(Value::Null),
21 }
22 });
23
24 rp.register_builtin("regex.matchAll", |args, _| {
25 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
26 let pattern = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
27 let flags = args.get(2).map(|v| v.to_display_string()).unwrap_or_default();
28 let re = build_regex(&pattern, &flags)?;
29 let matches: Vec<Value> = re
30 .find_iter(&s)
31 .map(|m| Value::String(m.as_str().to_string()))
32 .collect();
33 Ok(Value::Array(matches))
34 });
35
36 rp.register_builtin("regex.replace", |args, _| {
37 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
38 let pattern = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
39 let replacement = args.get(2).map(|v| v.to_display_string()).unwrap_or_default();
40 let flags = args.get(3).map(|v| v.to_display_string()).unwrap_or_default();
41 let re = build_regex(&pattern, &flags)?;
42 if flags.contains('g') {
43 Ok(Value::String(re.replace_all(&s, replacement.as_str()).to_string()))
44 } else {
45 Ok(Value::String(re.replace(&s, replacement.as_str()).to_string()))
46 }
47 });
48
49 rp.register_builtin("regex.split", |args, _| {
50 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
51 let pattern = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
52 let re = build_regex(&pattern, "")?;
53 let parts: Vec<Value> = re
54 .split(&s)
55 .map(|p| Value::String(p.to_string()))
56 .collect();
57 Ok(Value::Array(parts))
58 });
59
60 rp.register_builtin("regex.capture", |args, _| {
61 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
62 let pattern = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
63 let flags = args.get(2).map(|v| v.to_display_string()).unwrap_or_default();
64 let re = build_regex(&pattern, &flags)?;
65 match re.captures(&s) {
66 Some(caps) => {
67 let groups: Vec<Value> = caps
68 .iter()
69 .map(|m| match m {
70 Some(m) => Value::String(m.as_str().to_string()),
71 None => Value::Null,
72 })
73 .collect();
74 Ok(Value::Array(groups))
75 }
76 None => Ok(Value::Null),
77 }
78 });
79
80 rp.register_builtin("regex.escape", |args, _| {
81 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
82 Ok(Value::String(regex::escape(&s)))
83 });
84}
85
86fn build_regex(pattern: &str, flags: &str) -> Result<Regex, String> {
87 let case_insensitive = flags.contains('i');
88 let multiline = flags.contains('m');
89 let dotall = flags.contains('s');
90
91 let mut prefix = String::new();
92 if case_insensitive || multiline || dotall {
93 prefix.push_str("(?");
94 if case_insensitive {
95 prefix.push('i');
96 }
97 if multiline {
98 prefix.push('m');
99 }
100 if dotall {
101 prefix.push('s');
102 }
103 prefix.push(')');
104 }
105
106 let full_pattern = format!("{}{}", prefix, pattern);
107 Regex::new(&full_pattern).map_err(|e| format!("regex error: {}", e))
108}