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 Value::ErrorMsg(e, m) => return Value::ErrorMsg(e.clone(), m.clone()),
16 _ => return Value::Error(ErrorKind::Value),
17 };
18 let pattern = match to_string_val(args[1].clone()) {
19 Ok(s) => s,
20 Err(e) => return e,
21 };
22 let replacement = match to_string_val(args[2].clone()) {
23 Ok(s) => s,
24 Err(e) => return e,
25 };
26 let re = match Regex::new(&pattern) {
27 Ok(r) => r,
28 Err(_) => return Value::Error(ErrorKind::Ref),
29 };
30 let text_bytes = text.as_bytes();
33 let text_len = text_bytes.len();
34 let mut result = String::new();
35 let mut pos = 0usize;
36 while pos <= text_len {
37 let slice = &text[pos..];
38 if let Some(m) = re.find(slice) {
39 let abs_start = pos + m.start();
40 let abs_end = pos + m.end();
41 result.push_str(&text[pos..abs_start]);
42 result.push_str(&replacement);
43 if m.start() == m.end() {
44 if abs_end < text_len {
46 let next_char_end = text[abs_end..].chars().next()
49 .map(|c| abs_end + c.len_utf8())
50 .unwrap_or(text_len);
51 result.push_str(&text[abs_end..next_char_end]);
52 pos = next_char_end;
53 } else {
54 pos = abs_end + 1;
55 }
56 } else {
57 pos = abs_end;
58 }
59 } else {
60 result.push_str(&text[pos..]);
61 break;
62 }
63 }
64 Value::Text(result)
65}
66
67#[cfg(test)]
68mod tests;