Skip to main content

robinpath_modules/modules/
regex_mod.rs

1use 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}