Skip to main content

windjammer_runtime/
regex_mod.rs

1//! Regular expressions
2//!
3//! Windjammer's `std::regex` module maps to these functions.
4
5use regex::Regex as RegexImpl;
6use regex::RegexBuilder;
7
8/// Re-export Regex type for use in Windjammer code
9pub use regex::Regex;
10
11/// Create a new regex (for reuse across multiple operations)
12pub fn new(pattern: &str) -> Result<Regex, String> {
13    RegexImpl::new(pattern).map_err(|e| e.to_string())
14}
15
16/// Compile a regex pattern (alias for new)
17pub fn compile(pattern: &str) -> Result<Regex, String> {
18    RegexImpl::new(pattern).map_err(|e| e.to_string())
19}
20
21/// Compile a regex pattern with flags
22/// Flags: "i" = case insensitive, "m" = multiline, "s" = dot matches newline
23pub fn compile_with_flags(pattern: &str, flags: &str) -> Result<Regex, String> {
24    let mut builder = RegexBuilder::new(pattern);
25
26    for flag in flags.chars() {
27        match flag {
28            'i' => {
29                builder.case_insensitive(true);
30            }
31            'm' => {
32                builder.multi_line(true);
33            }
34            's' => {
35                builder.dot_matches_new_line(true);
36            }
37            _ => return Err(format!("Unknown regex flag: {}", flag)),
38        }
39    }
40
41    builder.build().map_err(|e| e.to_string())
42}
43
44/// Escape special regex characters in a string
45pub fn escape(text: &str) -> String {
46    regex::escape(text)
47}
48
49/// Check if pattern matches string (compiles regex each time - use new() for better performance)
50pub fn is_match(pattern: &str, text: &str) -> Result<bool, String> {
51    let re = Regex::new(pattern).map_err(|e| e.to_string())?;
52    Ok(re.is_match(text))
53}
54
55/// Check if regex matches string (with pre-compiled regex)
56pub fn is_match_compiled(re: &Regex, text: &str) -> bool {
57    re.is_match(text)
58}
59
60/// Find first match
61pub fn find(pattern: &str, text: &str) -> Result<Option<String>, String> {
62    let re = Regex::new(pattern).map_err(|e| e.to_string())?;
63    Ok(re.find(text).map(|m| m.as_str().to_string()))
64}
65
66/// Find first match (with pre-compiled regex)
67pub fn find_compiled(re: &Regex, text: &str) -> Option<String> {
68    re.find(text).map(|m| m.as_str().to_string())
69}
70
71/// Find all matches
72pub fn find_all(pattern: &str, text: &str) -> Result<Vec<String>, String> {
73    let re = Regex::new(pattern).map_err(|e| e.to_string())?;
74    Ok(re.find_iter(text).map(|m| m.as_str().to_string()).collect())
75}
76
77/// Find all matches (with pre-compiled regex)
78pub fn find_all_compiled(re: &Regex, text: &str) -> Vec<String> {
79    re.find_iter(text).map(|m| m.as_str().to_string()).collect()
80}
81
82/// Replace first match
83pub fn replace(pattern: &str, text: &str, replacement: &str) -> Result<String, String> {
84    let re = Regex::new(pattern).map_err(|e| e.to_string())?;
85    Ok(re.replace(text, replacement).to_string())
86}
87
88/// Replace first match (with pre-compiled regex)
89pub fn replace_compiled(re: &Regex, text: &str, replacement: &str) -> String {
90    re.replace(text, replacement).to_string()
91}
92
93/// Replace all matches
94pub fn replace_all(pattern: &str, text: &str, replacement: &str) -> Result<String, String> {
95    let re = Regex::new(pattern).map_err(|e| e.to_string())?;
96    Ok(re.replace_all(text, replacement).to_string())
97}
98
99/// Replace all matches (with pre-compiled regex)
100pub fn replace_all_compiled(re: &Regex, text: &str, replacement: &str) -> String {
101    re.replace_all(text, replacement).to_string()
102}
103
104/// Split string by regex pattern
105pub fn split(pattern: &str, text: &str) -> Result<Vec<String>, String> {
106    let re = Regex::new(pattern).map_err(|e| e.to_string())?;
107    Ok(re.split(text).map(|s| s.to_string()).collect())
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn test_is_match() {
116        assert!(is_match(r"\d+", "123").unwrap());
117        assert!(!is_match(r"\d+", "abc").unwrap());
118    }
119
120    #[test]
121    fn test_find() {
122        assert_eq!(find(r"\d+", "abc123def").unwrap(), Some("123".to_string()));
123        assert_eq!(find(r"\d+", "abc").unwrap(), None);
124    }
125
126    #[test]
127    fn test_find_all() {
128        let matches = find_all(r"\d+", "a1b2c3").unwrap();
129        assert_eq!(matches, vec!["1", "2", "3"]);
130    }
131
132    #[test]
133    fn test_replace() {
134        assert_eq!(replace(r"\d+", "abc123def", "X").unwrap(), "abcXdef");
135    }
136
137    #[test]
138    fn test_replace_all() {
139        assert_eq!(replace_all(r"\d+", "a1b2c3", "X").unwrap(), "aXbXcX");
140    }
141
142    #[test]
143    fn test_split() {
144        let parts = split(r"\s+", "hello  world\t\tfoo").unwrap();
145        assert_eq!(parts, vec!["hello", "world", "foo"]);
146    }
147}