1use seq_core::seqstring::global_string;
35use seq_core::stack::{Stack, pop, push};
36use seq_core::value::{Value, VariantData};
37
38use regex::Regex;
39use std::sync::Arc;
40
41fn make_list(items: Vec<Value>) -> Value {
43 Value::Variant(Arc::new(VariantData::new(
44 global_string("List".to_string()),
45 items,
46 )))
47}
48
49#[unsafe(no_mangle)]
56pub unsafe extern "C" fn patch_seq_regex_match(stack: Stack) -> Stack {
57 assert!(!stack.is_null(), "regex.match?: stack is empty");
58
59 let (stack, pattern_val) = unsafe { pop(stack) };
60 let (stack, text_val) = unsafe { pop(stack) };
61
62 match (text_val, pattern_val) {
63 (Value::String(text), Value::String(pattern)) => {
64 let result = match Regex::new(pattern.as_str()) {
65 Ok(re) => re.is_match(text.as_str()),
66 Err(_) => false, };
68 unsafe { push(stack, Value::Bool(result)) }
69 }
70 _ => panic!("regex.match?: expected two Strings on stack"),
71 }
72}
73
74#[unsafe(no_mangle)]
83pub unsafe extern "C" fn patch_seq_regex_find(stack: Stack) -> Stack {
84 assert!(!stack.is_null(), "regex.find: stack is empty");
85
86 let (stack, pattern_val) = unsafe { pop(stack) };
87 let (stack, text_val) = unsafe { pop(stack) };
88
89 match (text_val, pattern_val) {
90 (Value::String(text), Value::String(pattern)) => {
91 match Regex::new(pattern.as_str()) {
92 Ok(re) => match re.find(text.as_str()) {
93 Some(m) => {
94 let stack = unsafe {
95 push(stack, Value::String(global_string(m.as_str().to_string())))
96 };
97 unsafe { push(stack, Value::Bool(true)) }
98 }
99 None => {
100 let stack =
101 unsafe { push(stack, Value::String(global_string(String::new()))) };
102 unsafe { push(stack, Value::Bool(false)) }
103 }
104 },
105 Err(_) => {
106 let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
108 unsafe { push(stack, Value::Bool(false)) }
109 }
110 }
111 }
112 _ => panic!("regex.find: expected two Strings on stack"),
113 }
114}
115
116#[unsafe(no_mangle)]
126pub unsafe extern "C" fn patch_seq_regex_find_all(stack: Stack) -> Stack {
127 assert!(!stack.is_null(), "regex.find-all: stack is empty");
128
129 let (stack, pattern_val) = unsafe { pop(stack) };
130 let (stack, text_val) = unsafe { pop(stack) };
131
132 match (text_val, pattern_val) {
133 (Value::String(text), Value::String(pattern)) => match Regex::new(pattern.as_str()) {
134 Ok(re) => {
135 let matches: Vec<Value> = re
136 .find_iter(text.as_str())
137 .map(|m| Value::String(global_string(m.as_str().to_string())))
138 .collect();
139 let stack = unsafe { push(stack, make_list(matches)) };
140 unsafe { push(stack, Value::Bool(true)) }
141 }
142 Err(_) => {
143 let stack = unsafe { push(stack, make_list(vec![])) };
145 unsafe { push(stack, Value::Bool(false)) }
146 }
147 },
148 _ => panic!("regex.find-all: expected two Strings on stack"),
149 }
150}
151
152#[unsafe(no_mangle)]
162pub unsafe extern "C" fn patch_seq_regex_replace(stack: Stack) -> Stack {
163 assert!(!stack.is_null(), "regex.replace: stack is empty");
164
165 let (stack, replacement_val) = unsafe { pop(stack) };
166 let (stack, pattern_val) = unsafe { pop(stack) };
167 let (stack, text_val) = unsafe { pop(stack) };
168
169 match (text_val, pattern_val, replacement_val) {
170 (Value::String(text), Value::String(pattern), Value::String(replacement)) => {
171 match Regex::new(pattern.as_str()) {
172 Ok(re) => {
173 let result = re.replace(text.as_str(), replacement.as_str()).into_owned();
174 let stack = unsafe { push(stack, Value::String(global_string(result))) };
175 unsafe { push(stack, Value::Bool(true)) }
176 }
177 Err(_) => {
178 let stack = unsafe {
180 push(
181 stack,
182 Value::String(global_string(text.as_str().to_string())),
183 )
184 };
185 unsafe { push(stack, Value::Bool(false)) }
186 }
187 }
188 }
189 _ => panic!("regex.replace: expected three Strings on stack"),
190 }
191}
192
193#[unsafe(no_mangle)]
203pub unsafe extern "C" fn patch_seq_regex_replace_all(stack: Stack) -> Stack {
204 assert!(!stack.is_null(), "regex.replace-all: stack is empty");
205
206 let (stack, replacement_val) = unsafe { pop(stack) };
207 let (stack, pattern_val) = unsafe { pop(stack) };
208 let (stack, text_val) = unsafe { pop(stack) };
209
210 match (text_val, pattern_val, replacement_val) {
211 (Value::String(text), Value::String(pattern), Value::String(replacement)) => {
212 match Regex::new(pattern.as_str()) {
213 Ok(re) => {
214 let result = re
215 .replace_all(text.as_str(), replacement.as_str())
216 .into_owned();
217 let stack = unsafe { push(stack, Value::String(global_string(result))) };
218 unsafe { push(stack, Value::Bool(true)) }
219 }
220 Err(_) => {
221 let stack = unsafe {
223 push(
224 stack,
225 Value::String(global_string(text.as_str().to_string())),
226 )
227 };
228 unsafe { push(stack, Value::Bool(false)) }
229 }
230 }
231 }
232 _ => panic!("regex.replace-all: expected three Strings on stack"),
233 }
234}
235
236#[unsafe(no_mangle)]
246pub unsafe extern "C" fn patch_seq_regex_captures(stack: Stack) -> Stack {
247 assert!(!stack.is_null(), "regex.captures: stack is empty");
248
249 let (stack, pattern_val) = unsafe { pop(stack) };
250 let (stack, text_val) = unsafe { pop(stack) };
251
252 match (text_val, pattern_val) {
253 (Value::String(text), Value::String(pattern)) => {
254 match Regex::new(pattern.as_str()) {
255 Ok(re) => match re.captures(text.as_str()) {
256 Some(caps) => {
257 let groups: Vec<Value> = caps
259 .iter()
260 .skip(1)
261 .map(|m| match m {
262 Some(m) => Value::String(global_string(m.as_str().to_string())),
263 None => Value::String(global_string(String::new())),
264 })
265 .collect();
266 let stack = unsafe { push(stack, make_list(groups)) };
267 unsafe { push(stack, Value::Bool(true)) }
268 }
269 None => {
270 let stack = unsafe { push(stack, make_list(vec![])) };
271 unsafe { push(stack, Value::Bool(false)) }
272 }
273 },
274 Err(_) => {
275 let stack = unsafe { push(stack, make_list(vec![])) };
277 unsafe { push(stack, Value::Bool(false)) }
278 }
279 }
280 }
281 _ => panic!("regex.captures: expected two Strings on stack"),
282 }
283}
284
285#[unsafe(no_mangle)]
295pub unsafe extern "C" fn patch_seq_regex_split(stack: Stack) -> Stack {
296 assert!(!stack.is_null(), "regex.split: stack is empty");
297
298 let (stack, pattern_val) = unsafe { pop(stack) };
299 let (stack, text_val) = unsafe { pop(stack) };
300
301 match (text_val, pattern_val) {
302 (Value::String(text), Value::String(pattern)) => match Regex::new(pattern.as_str()) {
303 Ok(re) => {
304 let parts: Vec<Value> = re
305 .split(text.as_str())
306 .map(|s| Value::String(global_string(s.to_string())))
307 .collect();
308 let stack = unsafe { push(stack, make_list(parts)) };
309 unsafe { push(stack, Value::Bool(true)) }
310 }
311 Err(_) => {
312 let parts = vec![Value::String(global_string(text.as_str().to_string()))];
314 let stack = unsafe { push(stack, make_list(parts)) };
315 unsafe { push(stack, Value::Bool(false)) }
316 }
317 },
318 _ => panic!("regex.split: expected two Strings on stack"),
319 }
320}
321
322#[unsafe(no_mangle)]
331pub unsafe extern "C" fn patch_seq_regex_valid(stack: Stack) -> Stack {
332 assert!(!stack.is_null(), "regex.valid?: stack is empty");
333
334 let (stack, pattern_val) = unsafe { pop(stack) };
335
336 match pattern_val {
337 Value::String(pattern) => {
338 let is_valid = Regex::new(pattern.as_str()).is_ok();
339 unsafe { push(stack, Value::Bool(is_valid)) }
340 }
341 _ => panic!("regex.valid?: expected String on stack"),
342 }
343}
344
345#[cfg(test)]
346mod tests;