truecalc_core/eval/functions/text/replaceb/
mod.rs1use crate::eval::coercion::{to_number, to_string_val};
2use crate::eval::functions::check_arity;
3use crate::eval::functions::text::lenb::dbcs_char_width;
4use crate::types::{ErrorKind, Value};
5
6fn dbcs_start_to_char_idx(s: &str, start_1based: usize) -> usize {
8 if start_1based <= 1 {
9 return 0;
10 }
11 let target = start_1based - 1; let mut pos = 0usize;
13 for (i, c) in s.chars().enumerate() {
14 let w = dbcs_char_width(c);
15 if pos >= target {
16 return i;
17 }
18 if pos < target && pos + w > target {
19 return i + 1; }
21 pos += w;
22 }
23 s.chars().count()
24}
25
26fn char_idx_to_dbcs_byte_end(s: &str, char_idx: usize) -> usize {
28 s.chars().take(char_idx).map(dbcs_char_width).sum::<usize>()
29}
30
31pub fn replaceb_fn(args: &[Value]) -> Value {
33 if let Some(err) = check_arity(args, 4, 4) {
34 return err;
35 }
36 let text = match to_string_val(args[0].clone()) {
37 Ok(s) => s,
38 Err(e) => return e,
39 };
40 let start_num = match to_number(args[1].clone()) {
41 Ok(n) => n,
42 Err(e) => return e,
43 };
44 let num_bytes = match to_number(args[2].clone()) {
45 Ok(n) => n,
46 Err(e) => return e,
47 };
48 let new_text = match to_string_val(args[3].clone()) {
49 Ok(s) => s,
50 Err(e) => return e,
51 };
52 if start_num < 1.0 || num_bytes < 0.0 {
53 return Value::Error(ErrorKind::Value);
54 }
55 let start_dbcs = start_num as usize; let num = num_bytes as usize;
57 let chars: Vec<char> = text.chars().collect();
58 let start_char = dbcs_start_to_char_idx(&text, start_dbcs);
60 let start_dbcs_snapped = char_idx_to_dbcs_byte_end(&text, start_char) + 1; let end_dbcs_target = start_dbcs_snapped.saturating_sub(1) + num; let mut taken = 0usize;
65 let mut end_char = start_char;
66 for c in chars[start_char..].iter() {
67 let w = dbcs_char_width(*c);
68 if taken + w <= num {
69 taken += w;
70 end_char += 1;
71 if taken == num { break; }
72 } else {
73 break;
75 }
76 }
77 let prefix: String = chars[..start_char].iter().collect();
79 let suffix: String = chars[end_char..].iter().collect();
80 let _ = end_dbcs_target; Value::Text(format!("{}{}{}", prefix, new_text, suffix))
82}
83
84#[cfg(test)]
85mod tests;