1use std::cmp;
7use std::collections::HashMap;
8use std::sync::LazyLock;
9
10use regex::Regex;
11
12use crate::constants::{MAX_COLUMNS, MIN_COLUMNS, TOTAL_ROWS};
13use crate::errors::{
14 ErrColumnNumber, ErrCoordinates, ErrMaxRows, ErrParameterInvalid,
15 new_coordinates_to_cell_name_error, new_invalid_cell_name_error, new_invalid_column_name_error,
16 new_invalid_row_number_error,
17};
18
19pub fn split_cell_name(cell: &str) -> Result<(String, i32), String> {
27 let alpha = |r: char| ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || (r == '$');
28
29 if cell.chars().next().map_or(false, alpha) {
30 if let Some(i) = cell.rfind(alpha) {
31 if i < cell.len() - 1 {
32 let col = cell[..=i].replace('$', "");
33 let row_str = &cell[i + 1..];
34 if let Ok(row) = row_str.parse::<i32>() {
35 if row > 0 {
36 return Ok((col, row));
37 }
38 }
39 }
40 }
41 }
42 Err(new_invalid_cell_name_error(cell))
43}
44
45pub fn join_cell_name(col: &str, row: i32) -> Result<String, String> {
47 let norm_col: String = col
48 .chars()
49 .filter_map(|ch| {
50 if 'A' <= ch && ch <= 'Z' {
51 Some(ch)
52 } else if 'a' <= ch && ch <= 'z' {
53 Some((ch as u8 - b'a' + b'A') as char)
54 } else {
55 None
56 }
57 })
58 .collect();
59
60 if col.is_empty() || col.len() != norm_col.len() {
61 return Err(new_invalid_column_name_error(col));
62 }
63 if row < 1 {
64 return Err(new_invalid_row_number_error(row));
65 }
66 Ok(format!("{norm_col}{row}"))
67}
68
69pub fn column_name_to_number(name: &str) -> Result<i32, String> {
71 if name.is_empty() {
72 return Err(new_invalid_column_name_error(name));
73 }
74 let mut col = 0_i64;
75 let mut multi = 1_i64;
76 for r in name.bytes().rev() {
77 let value = if b'A' <= r && r <= b'Z' {
78 (r - b'A' + 1) as i64
79 } else if b'a' <= r && r <= b'z' {
80 (r - b'a' + 1) as i64
81 } else {
82 return Err(new_invalid_column_name_error(name));
83 };
84 col += value * multi;
85 multi *= 26;
86 }
87 if col > MAX_COLUMNS as i64 {
88 return Err(ErrColumnNumber.to_string());
89 }
90 Ok(col as i32)
91}
92
93static COLUMN_NAMES: LazyLock<Vec<String>> = LazyLock::new(|| {
94 let mut names = vec![String::new(); (MAX_COLUMNS + 1) as usize];
95 for i in 1..=MAX_COLUMNS {
96 let mut num = i;
97 let mut l = 0usize;
98 let mut n = i;
99 while n > 0 {
100 l += 1;
101 n = (n - 1) / 26;
102 }
103 let mut buf = vec![0u8; l as usize];
104 while num > 0 {
105 l -= 1;
106 buf[l] = ((num - 1) % 26) as u8 + b'A';
107 num = (num - 1) / 26;
108 }
109 names[i as usize] = String::from_utf8(buf).unwrap();
110 }
111 names
112});
113
114pub fn column_number_to_name(num: i32) -> Result<String, String> {
116 if num < MIN_COLUMNS || num > MAX_COLUMNS {
117 return Err(ErrColumnNumber.to_string());
118 }
119 Ok(COLUMN_NAMES[num as usize].clone())
120}
121
122pub fn cell_name_to_coordinates(cell: &str) -> Result<(i32, i32), String> {
124 let (col_name, row) = split_cell_name(cell)?;
125 if row > TOTAL_ROWS {
126 return Err(ErrMaxRows.to_string());
127 }
128 let col = column_name_to_number(&col_name)?;
129 Ok((col, row))
130}
131
132pub fn coordinates_to_cell_name(col: i32, row: i32, abs: bool) -> Result<String, String> {
134 if col < 1 || row < 1 {
135 return Err(new_coordinates_to_cell_name_error(col, row));
136 }
137 if row > TOTAL_ROWS {
138 return Err(ErrMaxRows.to_string());
139 }
140 let col_name = column_number_to_name(col)?;
141 if abs {
142 Ok(format!("${col_name}${row}"))
143 } else {
144 Ok(format!("{col_name}{row}"))
145 }
146}
147
148pub fn range_ref_to_coordinates(ref_str: &str) -> Result<Vec<i32>, String> {
150 let normalized = ref_str.replace('$', "");
151 let rng: Vec<&str> = normalized.split(':').collect();
152 if rng.len() < 2 {
153 return Err(ErrParameterInvalid.to_string());
154 }
155 cell_refs_to_coordinates(rng[0], rng[1])
156}
157
158pub fn cell_refs_to_coordinates(first_cell: &str, last_cell: &str) -> Result<Vec<i32>, String> {
160 let mut coordinates = vec![0; 4];
161 let (col, row) = cell_name_to_coordinates(first_cell)?;
162 coordinates[0] = col;
163 coordinates[1] = row;
164 let (col, row) = cell_name_to_coordinates(last_cell)?;
165 coordinates[2] = col;
166 coordinates[3] = row;
167 Ok(coordinates)
168}
169
170pub fn sort_coordinates(coordinates: &mut [i32]) -> Result<(), String> {
173 if coordinates.len() != 4 {
174 return Err(ErrCoordinates.to_string());
175 }
176 if coordinates[2] < coordinates[0] {
177 coordinates.swap(2, 0);
178 }
179 if coordinates[3] < coordinates[1] {
180 coordinates.swap(3, 1);
181 }
182 Ok(())
183}
184
185pub fn coordinates_to_range_ref(coordinates: &[i32], abs: bool) -> Result<String, String> {
187 if coordinates.len() != 4 {
188 return Err(ErrCoordinates.to_string());
189 }
190 let first = coordinates_to_cell_name(coordinates[0], coordinates[1], abs)?;
191 let last = coordinates_to_cell_name(coordinates[2], coordinates[3], abs)?;
192 Ok(format!("{first}:{last}"))
193}
194
195pub fn flat_sqref(sqref: &str) -> Result<std::collections::HashMap<i32, Vec<Vec<i32>>>, String> {
198 let mut cells: std::collections::HashMap<i32, Vec<Vec<i32>>> = std::collections::HashMap::new();
199 for r#ref in sqref.split_whitespace() {
200 let rng: Vec<&str> = r#ref.split(':').collect();
201 match rng.len() {
202 1 => {
203 let (col, row) = cell_name_to_coordinates(rng[0])?;
204 cells.entry(col).or_default().push(vec![col, row]);
205 }
206 2 => {
207 let mut coordinates = range_ref_to_coordinates(r#ref)?;
208 sort_coordinates(&mut coordinates)?;
209 for c in coordinates[0]..=coordinates[2] {
210 for r in coordinates[1]..=coordinates[3] {
211 cells.entry(c).or_default().push(vec![c, r]);
212 }
213 }
214 }
215 _ => return Err(ErrParameterInvalid.to_string()),
216 }
217 }
218 for col_cells in cells.values_mut() {
219 col_cells.sort_by_key(|c| c[1]);
220 }
221 Ok(cells)
222}
223
224pub fn in_coordinates(a: &[Vec<i32>], x: &[i32]) -> i32 {
226 for (idx, n) in a.iter().enumerate() {
227 if x.len() >= 2 && n.len() >= 2 && x[0] == n[0] && x[1] == n[1] {
228 return idx as i32;
229 }
230 }
231 -1
232}
233
234pub fn in_str_slice<T: AsRef<str>>(a: &[T], x: &str, case_sensitive: bool) -> i32 {
240 for (idx, n) in a.iter().enumerate() {
241 let n = n.as_ref();
242 if (!case_sensitive && n.eq_ignore_ascii_case(x)) || (case_sensitive && n == x) {
243 return idx as i32;
244 }
245 }
246 -1
247}
248
249pub fn in_float64_slice(a: &[f64], x: f64) -> i32 {
251 for (idx, n) in a.iter().enumerate() {
252 if *n == x {
253 return idx as i32;
254 }
255 }
256 -1
257}
258
259pub fn is_numeric(s: &str) -> (bool, i32, f64) {
266 if s.contains('_') {
267 return (false, 0, 0.0);
268 }
269 if let Ok(flt) = s.parse::<f64>() {
270 let no_scientific = format!("{flt:.15}").trim_end_matches('0').to_string();
271 let precision = no_scientific.len() as i32 - no_scientific.matches('.').count() as i32;
272 return (true, precision, flt);
273 }
274 (false, 0, 0.0)
275}
276
277pub fn count_utf16_string(s: &str) -> usize {
283 s.chars().map(|r| r.len_utf16()).sum()
284}
285
286pub fn truncate_utf16_units(s: &str, max: usize) -> String {
288 let mut cnt = 0usize;
289 s.chars()
290 .take_while(|r| {
291 let len = r.len_utf16();
292 if cnt + len > max {
293 return false;
294 }
295 cnt += len;
296 true
297 })
298 .collect()
299}
300
301pub fn str_map(pairs: &[(&str, &str)]) -> HashMap<String, String> {
307 pairs
308 .iter()
309 .map(|(k, v)| (k.to_string(), v.to_string()))
310 .collect()
311}
312
313pub fn bool_ptr(b: bool) -> Option<bool> {
315 Some(b)
316}
317
318pub fn int_ptr(i: i64) -> Option<i64> {
320 Some(i)
321}
322
323pub fn string_ptr(s: impl Into<String>) -> Option<String> {
325 Some(s.into())
326}
327
328pub fn uint_ptr(u: u32) -> Option<u32> {
330 Some(u)
331}
332
333pub fn float64_ptr(f: f64) -> Option<f64> {
335 Some(f)
336}
337
338static BSTR_EXP: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"_x[a-fA-F\d]{4}_").unwrap());
343static BSTR_ESCAPE_EXP: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"x[a-fA-F\d]{4}_").unwrap());
344
345fn unquote_unicode_hex(hex: &str) -> Option<String> {
346 let code = u32::from_str_radix(hex, 16).ok()?;
347 char::from_u32(code).map(|c| c.to_string())
348}
349
350pub fn bstr_unmarshal(s: &str) -> String {
353 if !s.contains("_x") {
354 return s.to_string();
355 }
356 let mut result = String::with_capacity(s.len());
357 let mut cursor = 0;
358 for m in BSTR_EXP.find_iter(s) {
359 result.push_str(&s[cursor..m.start()]);
360 let sub = m.as_str();
361 if sub == "_x005F_" {
362 cursor = m.end();
363 result.push('_');
364 continue;
365 }
366 if let Some(ch) = unquote_unicode_hex(&s[m.start() + 2..m.end() - 1]) {
367 cursor = m.end();
368 result.push_str(&ch);
369 }
370 }
371 if cursor < s.len() {
372 result.push_str(&s[cursor..]);
373 }
374 result
375}
376
377pub fn bstr_marshal(s: &str) -> String {
380 let mut result = String::with_capacity(s.len());
381 let mut cursor = 0;
382 let len = s.len();
383 for m in BSTR_EXP.find_iter(s) {
384 result.push_str(&s[cursor..m.start()]);
385 let sub = m.as_str();
386 if sub == "_x005F_" {
387 cursor = m.end();
388 if cursor + 6 <= len && BSTR_ESCAPE_EXP.is_match(&s[cursor..cursor + 6]) {
389 let hex = &s[cursor + 1..cursor + 5];
390 if unquote_unicode_hex(hex).is_some() {
391 result.push_str(sub);
392 result.push_str("x005F");
393 result.push_str(sub);
394 continue;
395 }
396 }
397 result.push_str(sub);
398 result.push_str("x005F_");
399 continue;
400 }
401 if unquote_unicode_hex(&s[m.start() + 2..m.end() - 1]).is_some() {
402 cursor = m.end();
403 result.push_str("_x005F");
404 result.push_str(sub);
405 }
406 }
407 if cursor < s.len() {
408 result.push_str(&s[cursor..]);
409 }
410 result
411}
412
413pub fn float_to_fraction(
420 x: f64,
421 numerator_placeholder: i64,
422 denominator_placeholder: i64,
423) -> String {
424 if denominator_placeholder <= 0 {
425 return String::new();
426 }
427 let denominator_limit = 10_i64.pow(denominator_placeholder as u32);
428 let (num, den) = float_to_frac_use_continued_fraction(x, denominator_limit);
429 if num == 0 {
430 return " ".repeat((numerator_placeholder + denominator_placeholder + 1) as usize);
431 }
432 let num_str = num.to_string();
433 let den_str = den.to_string();
434 let numerator_pad = cmp::max(numerator_placeholder - num_str.len() as i64, 0) as usize;
435 let denominator_pad = cmp::max(denominator_placeholder - den_str.len() as i64, 0) as usize;
436 format!(
437 "{}{}/{}{}",
438 " ".repeat(numerator_pad),
439 num_str,
440 den_str,
441 " ".repeat(denominator_pad)
442 )
443}
444
445pub fn float_to_frac_use_continued_fraction(r: f64, denominator_limit: i64) -> (i64, i64) {
448 let mut p1: i128 = 1;
451 let mut q1: i128 = 0;
452 let mut p2: i128 = 0;
453 let mut q2: i128 = 1;
454 let mut last_a: i128 = 0;
455 let mut last_b: i128 = 0;
456 let mut r = r;
457 let limit = denominator_limit as i128;
458 loop {
459 let a = r.floor() as i128;
460 let cur_a = a * p1 + p2;
461 let cur_b = a * q1 + q2;
462 p2 = p1;
463 q2 = q1;
464 p1 = cur_a;
465 q1 = cur_b;
466 let frac = r - a as f64;
467 if q1 >= limit {
468 return (last_a as i64, last_b as i64);
469 }
470 if frac.abs() < 1e-12 {
471 return (cur_a as i64, cur_b as i64);
472 }
473 last_a = cur_a;
474 last_b = cur_b;
475 r = 1.0 / frac;
476 }
477}
478
479#[derive(Debug, Default, Clone, PartialEq, Eq)]
485pub struct Stack<T> {
486 items: Vec<T>,
487}
488
489impl<T> Stack<T> {
490 pub fn new() -> Self {
492 Self { items: Vec::new() }
493 }
494
495 pub fn push(&mut self, value: T) {
497 self.items.push(value);
498 }
499
500 pub fn pop(&mut self) -> Option<T> {
502 self.items.pop()
503 }
504
505 pub fn peek(&self) -> Option<&T> {
507 self.items.last()
508 }
509
510 pub fn len(&self) -> usize {
512 self.items.len()
513 }
514
515 pub fn empty(&self) -> bool {
517 self.items.is_empty()
518 }
519
520 pub fn is_empty(&self) -> bool {
522 self.items.len() == 0
523 }
524}
525
526impl<T> FromIterator<T> for Stack<T> {
527 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
528 Self {
529 items: Vec::from_iter(iter),
530 }
531 }
532}
533
534#[cfg(test)]
535mod tests {
536 use super::*;
537
538 #[test]
539 fn test_bstr_unmarshal() {
540 let cases = [
541 ("*_x0008_", "*\u{0008}"),
542 ("*_x0008_*", "*\u{0008}*"),
543 ("*_x005F__x0008_*", "*_\u{0008}*"),
544 ("*_x005F_x0001_*", "*_x0001_*"),
545 ("*_x005f_x005F__x0008_*", "*_x005F_\u{0008}*"),
546 ("*_x005F_x005F_xG05F_x0006_*", "*_x005F_xG05F\u{0006}*"),
547 ("*_x005F_x005F_x005F_x0006_*", "*_x005F_x0006_*"),
548 ("_x005F__x0008_******", "_\u{0008}******"),
549 ("******_x005F__x0008_", "******_\u{0008}"),
550 ("_x000x_x005F_x000x_", "_x000x_x000x_"),
551 ];
552 for (input, expected) in cases {
553 assert_eq!(bstr_unmarshal(input), expected, "input: {}", input);
554 }
555 }
556
557 #[test]
558 fn test_bstr_marshal() {
559 let cases = [
560 ("*_x0008_*", "*_x005F_x0008_*"),
561 ("*_x005F_*", "*_x005F_x005F_*"),
562 ("*_x005F_xG006_*", "*_x005F_x005F_xG006_*"),
563 ("*_x005F_x0006_*", "*_x005F_x005F_x005F_x0006_*"),
564 ];
565 for (input, expected) in cases {
566 assert_eq!(bstr_marshal(input), expected, "input: {}", input);
567 }
568 }
569
570 #[test]
571 fn test_float_to_fraction() {
572 assert_eq!(float_to_fraction(0.19, 0, 0), "");
573 assert_eq!(float_to_fraction(0.19, 1, 1), "1/5");
574 assert_eq!(float_to_fraction(0.9999, 10, 10).trim(), "9999/10000");
575 assert_eq!(
576 float_to_fraction(std::f64::consts::E, 1, 18),
577 "954888175898973913/351283728530932463"
578 );
579 }
580
581 #[test]
582 fn test_float_to_frac_use_continued_fraction() {
583 assert_eq!(float_to_frac_use_continued_fraction(0.19, 10), (1, 5));
584 assert_eq!(
585 float_to_frac_use_continued_fraction(0.9999, 10_000_000_000),
586 (9999, 10000)
587 );
588 }
589
590 #[test]
591 fn test_count_and_truncate_utf16() {
592 let s = "a\u{10000}b"; assert_eq!(count_utf16_string(s), 4);
594 assert_eq!(truncate_utf16_units(s, 1), "a");
595 assert_eq!(truncate_utf16_units(s, 2), "a");
596 assert_eq!(truncate_utf16_units(s, 3), "a\u{10000}");
597 assert_eq!(truncate_utf16_units(s, 4), s);
598 }
599
600 #[test]
601 fn test_ptr_helpers() {
602 assert_eq!(uint_ptr(42), Some(42));
603 assert_eq!(float64_ptr(1.5), Some(1.5));
604 assert_eq!(bool_ptr(false), Some(false));
605 assert_eq!(int_ptr(-7), Some(-7));
606 }
607
608 #[test]
609 fn test_stack() {
610 let mut stack = Stack::new();
611 assert!(stack.is_empty());
612 assert!(stack.empty());
613 stack.push(1);
614 stack.push(2);
615 stack.push(3);
616 assert_eq!(stack.len(), 3);
617 assert_eq!(stack.peek(), Some(&3));
618 assert_eq!(stack.pop(), Some(3));
619 assert_eq!(stack.pop(), Some(2));
620 assert_eq!(stack.len(), 1);
621 assert!(!stack.is_empty());
622 assert_eq!(stack.pop(), Some(1));
623 assert_eq!(stack.pop(), None);
624 }
625}