truecalc_core/eval/functions/text/regexreplace/
mod.rs1use crate::eval::coercion::to_string_val;
2use crate::eval::functions::check_arity;
3use crate::types::{ErrorKind, Value};
4use regex_lite::Regex;
5
6pub fn regexreplace_fn(args: &[Value]) -> Value {
10 if let Some(err) = check_arity(args, 3, 3) { return err; }
11 let text = match &args[0] {
12 Value::Text(s) => s.clone(),
13 Value::Empty => String::new(),
14 Value::Error(e) => return Value::Error(e.clone()),
15 _ => return Value::Error(ErrorKind::Value),
16 };
17 let pattern = match to_string_val(args[1].clone()) {
18 Ok(s) => s,
19 Err(e) => return e,
20 };
21 let replacement = match to_string_val(args[2].clone()) {
22 Ok(s) => s,
23 Err(e) => return e,
24 };
25 let re = match Regex::new(&pattern) {
26 Ok(r) => r,
27 Err(_) => return Value::Error(ErrorKind::Ref),
28 };
29 let text_bytes = text.as_bytes();
32 let text_len = text_bytes.len();
33 let mut result = String::new();
34 let mut pos = 0usize;
35 while pos <= text_len {
36 let slice = &text[pos..];
37 if let Some(m) = re.find(slice) {
38 let abs_start = pos + m.start();
39 let abs_end = pos + m.end();
40 result.push_str(&text[pos..abs_start]);
41 result.push_str(&replacement);
42 if m.start() == m.end() {
43 if abs_end < text_len {
45 let next_char_end = text[abs_end..].chars().next()
48 .map(|c| abs_end + c.len_utf8())
49 .unwrap_or(text_len);
50 result.push_str(&text[abs_end..next_char_end]);
51 pos = next_char_end;
52 } else {
53 pos = abs_end + 1;
54 }
55 } else {
56 pos = abs_end;
57 }
58 } else {
59 result.push_str(&text[pos..]);
60 break;
61 }
62 }
63 Value::Text(result)
64}
65
66#[cfg(test)]
67mod tests;