ravenrs_sheet/calc/eval/
format.rs1use super::{CellError, Value};
2use crate::base::{CellRef, MAX_COLS, MAX_ROWS};
3
4pub(super) fn cell_format_code(code: &str) -> String {
6 let c = code.to_ascii_lowercase();
7 if c == "general" {
8 return "G".into();
9 }
10 if c.contains('%') {
11 return format!("P{}", decimals(&c));
12 }
13 if c.contains('e') && c.contains('0') {
14 return format!("S{}", decimals(&c));
15 }
16 if c.contains('$') {
17 return format!("C{}", decimals(&c));
18 }
19 if c.contains('y') || c.contains('d') {
20 return "D1".into();
21 }
22 if c.contains('h') {
23 return "D9".into();
24 }
25 if c.contains(',') {
26 return format!(",{}", decimals(&c));
27 }
28 format!("F{}", decimals(&c))
29}
30
31fn decimals(c: &str) -> usize {
32 c.split_once('.').map_or(0, |(_, d)| d.chars().take_while(|ch| *ch == '0').count())
33}
34
35pub(super) fn same_kind(a: &Value, b: &Value) -> bool {
36 std::mem::discriminant(a) == std::mem::discriminant(b) || (a.is_empty() || b.is_empty())
37}
38
39pub fn r1c1_to_a1(s: &str, at: CellRef) -> Result<Option<String>, CellError> {
42 let (sheet, body) = match s.rsplit_once('!') {
43 Some((sh, b)) => (format!("{sh}!"), b),
44 None => (String::new(), s),
45 };
46 let conv = |part: &str| -> Result<Option<String>, CellError> {
47 let u = part.to_ascii_uppercase();
48 let Some((rpart, cpart)) = u.strip_prefix('R').and_then(|rest| rest.split_once('C')) else { return Ok(None) };
49 let num = |p: &str, base: u32, max: u32| -> Option<Option<(u32, bool)>> {
51 let (v, absolute) = if p.is_empty() {
52 (Some(i64::from(base)), false)
53 } else if let Some(inner) = p.strip_prefix('[').and_then(|x| x.strip_suffix(']')) {
54 (i64::from(base).checked_add(inner.parse().ok()?), false)
55 } else {
56 (p.parse::<u32>().ok()?.checked_sub(1).map(i64::from), true)
57 };
58 Some(v.filter(|v| (0..i64::from(max)).contains(v)).map(|v| (v as u32, absolute)))
59 };
60 let (Some(r), Some(c)) = (num(rpart, at.row, MAX_ROWS), num(cpart, at.col, MAX_COLS)) else { return Ok(None) };
61 let (Some((r, ra)), Some((c, ca))) = (r, c) else { return Err(CellError::Ref) };
62 Ok(Some(format!("{}{}{}{}", if ca { "$" } else { "" }, crate::base::col_to_letters(c), if ra { "$" } else { "" }, r + 1)))
63 };
64 let out = match body.split_once(':') {
65 Some((a, b)) => match (conv(a)?, conv(b)?) {
66 (Some(a), Some(b)) => format!("{a}:{b}"),
67 _ => return Ok(None),
68 },
69 None => match conv(body)? {
70 Some(x) => x,
71 None => return Ok(None),
72 },
73 };
74 Ok(Some(format!("{sheet}{out}")))
75}