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_or_empty()) {
65 Ok(re) => re.is_match(text.as_str_or_empty()),
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_or_empty()) {
92 Ok(re) => match re.find(text.as_str_or_empty()) {
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)) => {
134 match Regex::new(pattern.as_str_or_empty()) {
135 Ok(re) => {
136 let matches: Vec<Value> = re
137 .find_iter(text.as_str_or_empty())
138 .map(|m| Value::String(global_string(m.as_str().to_string())))
139 .collect();
140 let stack = unsafe { push(stack, make_list(matches)) };
141 unsafe { push(stack, Value::Bool(true)) }
142 }
143 Err(_) => {
144 let stack = unsafe { push(stack, make_list(vec![])) };
146 unsafe { push(stack, Value::Bool(false)) }
147 }
148 }
149 }
150 _ => panic!("regex.find-all: expected two Strings on stack"),
151 }
152}
153
154#[unsafe(no_mangle)]
164pub unsafe extern "C" fn patch_seq_regex_replace(stack: Stack) -> Stack {
165 assert!(!stack.is_null(), "regex.replace: stack is empty");
166
167 let (stack, replacement_val) = unsafe { pop(stack) };
168 let (stack, pattern_val) = unsafe { pop(stack) };
169 let (stack, text_val) = unsafe { pop(stack) };
170
171 match (text_val, pattern_val, replacement_val) {
172 (Value::String(text), Value::String(pattern), Value::String(replacement)) => {
173 match Regex::new(pattern.as_str_or_empty()) {
174 Ok(re) => {
175 let result = re
176 .replace(text.as_str_or_empty(), replacement.as_str_or_empty())
177 .into_owned();
178 let stack = unsafe { push(stack, Value::String(global_string(result))) };
179 unsafe { push(stack, Value::Bool(true)) }
180 }
181 Err(_) => {
182 let stack = unsafe {
184 push(
185 stack,
186 Value::String(global_string(text.as_str_or_empty().to_string())),
187 )
188 };
189 unsafe { push(stack, Value::Bool(false)) }
190 }
191 }
192 }
193 _ => panic!("regex.replace: expected three Strings on stack"),
194 }
195}
196
197#[unsafe(no_mangle)]
207pub unsafe extern "C" fn patch_seq_regex_replace_all(stack: Stack) -> Stack {
208 assert!(!stack.is_null(), "regex.replace-all: stack is empty");
209
210 let (stack, replacement_val) = unsafe { pop(stack) };
211 let (stack, pattern_val) = unsafe { pop(stack) };
212 let (stack, text_val) = unsafe { pop(stack) };
213
214 match (text_val, pattern_val, replacement_val) {
215 (Value::String(text), Value::String(pattern), Value::String(replacement)) => {
216 match Regex::new(pattern.as_str_or_empty()) {
217 Ok(re) => {
218 let result = re
219 .replace_all(text.as_str_or_empty(), replacement.as_str_or_empty())
220 .into_owned();
221 let stack = unsafe { push(stack, Value::String(global_string(result))) };
222 unsafe { push(stack, Value::Bool(true)) }
223 }
224 Err(_) => {
225 let stack = unsafe {
227 push(
228 stack,
229 Value::String(global_string(text.as_str_or_empty().to_string())),
230 )
231 };
232 unsafe { push(stack, Value::Bool(false)) }
233 }
234 }
235 }
236 _ => panic!("regex.replace-all: expected three Strings on stack"),
237 }
238}
239
240#[unsafe(no_mangle)]
250pub unsafe extern "C" fn patch_seq_regex_captures(stack: Stack) -> Stack {
251 assert!(!stack.is_null(), "regex.captures: stack is empty");
252
253 let (stack, pattern_val) = unsafe { pop(stack) };
254 let (stack, text_val) = unsafe { pop(stack) };
255
256 match (text_val, pattern_val) {
257 (Value::String(text), Value::String(pattern)) => {
258 match Regex::new(pattern.as_str_or_empty()) {
259 Ok(re) => match re.captures(text.as_str_or_empty()) {
260 Some(caps) => {
261 let groups: Vec<Value> = caps
263 .iter()
264 .skip(1)
265 .map(|m| match m {
266 Some(m) => Value::String(global_string(m.as_str().to_string())),
267 None => Value::String(global_string(String::new())),
268 })
269 .collect();
270 let stack = unsafe { push(stack, make_list(groups)) };
271 unsafe { push(stack, Value::Bool(true)) }
272 }
273 None => {
274 let stack = unsafe { push(stack, make_list(vec![])) };
275 unsafe { push(stack, Value::Bool(false)) }
276 }
277 },
278 Err(_) => {
279 let stack = unsafe { push(stack, make_list(vec![])) };
281 unsafe { push(stack, Value::Bool(false)) }
282 }
283 }
284 }
285 _ => panic!("regex.captures: expected two Strings on stack"),
286 }
287}
288
289#[unsafe(no_mangle)]
299pub unsafe extern "C" fn patch_seq_regex_split(stack: Stack) -> Stack {
300 assert!(!stack.is_null(), "regex.split: stack is empty");
301
302 let (stack, pattern_val) = unsafe { pop(stack) };
303 let (stack, text_val) = unsafe { pop(stack) };
304
305 match (text_val, pattern_val) {
306 (Value::String(text), Value::String(pattern)) => {
307 match Regex::new(pattern.as_str_or_empty()) {
308 Ok(re) => {
309 let parts: Vec<Value> = re
310 .split(text.as_str_or_empty())
311 .map(|s| Value::String(global_string(s.to_string())))
312 .collect();
313 let stack = unsafe { push(stack, make_list(parts)) };
314 unsafe { push(stack, Value::Bool(true)) }
315 }
316 Err(_) => {
317 let parts = vec![Value::String(global_string(
319 text.as_str_or_empty().to_string(),
320 ))];
321 let stack = unsafe { push(stack, make_list(parts)) };
322 unsafe { push(stack, Value::Bool(false)) }
323 }
324 }
325 }
326 _ => panic!("regex.split: expected two Strings on stack"),
327 }
328}
329
330#[unsafe(no_mangle)]
339pub unsafe extern "C" fn patch_seq_regex_valid(stack: Stack) -> Stack {
340 assert!(!stack.is_null(), "regex.valid?: stack is empty");
341
342 let (stack, pattern_val) = unsafe { pop(stack) };
343
344 match pattern_val {
345 Value::String(pattern) => {
346 let is_valid = Regex::new(pattern.as_str_or_empty()).is_ok();
347 unsafe { push(stack, Value::Bool(is_valid)) }
348 }
349 _ => panic!("regex.valid?: expected String on stack"),
350 }
351}
352
353#[cfg(test)]
354mod tests;