use std::fmt;
use super::date::DateTimeValue;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CellRef {
pub col: u32,
pub row: u32,
}
impl CellRef {
pub fn parse(reference: &str) -> Option<Self> {
let bytes = reference.as_bytes();
let col_end = bytes
.iter()
.position(|b| b.is_ascii_digit())
.filter(|&p| p > 0)?;
let col_str = &reference[..col_end];
let row_str = &reference[col_end..];
let col = Self::parse_col(col_str)?;
let row: u32 = row_str.parse().ok()?;
if row == 0 {
return None;
}
Some(CellRef { col, row: row - 1 })
}
pub fn parse_col(col_str: &str) -> Option<u32> {
if col_str.is_empty() {
return None;
}
let mut result: u32 = 0;
for &b in col_str.as_bytes() {
if !b.is_ascii_alphabetic() {
return None;
}
let digit = (b.to_ascii_uppercase() - b'A') as u32;
result = result.checked_mul(26)?.checked_add(digit + 1)?;
}
Some(result - 1)
}
pub fn col_name(col: u32) -> String {
let mut result = Vec::new();
let mut n = col + 1; while n > 0 {
n -= 1;
result.push(b'A' + (n % 26) as u8);
n /= 26;
}
result.reverse();
String::from_utf8(result).unwrap()
}
}
impl fmt::Display for CellRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", Self::col_name(self.col), self.row + 1)
}
}
#[derive(Debug, Clone)]
pub enum CellValue {
Empty,
Number(f64),
String(String),
SharedString(u32),
Boolean(bool),
Error(String),
Date(DateTimeValue),
}
#[derive(Debug, Clone)]
pub struct Cell {
pub reference: CellRef,
pub value: CellValue,
pub style_index: Option<u32>,
pub formula: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_a1() {
let r = CellRef::parse("A1").unwrap();
assert_eq!(r.col, 0);
assert_eq!(r.row, 0);
}
#[test]
fn parse_z1() {
let r = CellRef::parse("Z1").unwrap();
assert_eq!(r.col, 25);
assert_eq!(r.row, 0);
}
#[test]
fn parse_aa1() {
let r = CellRef::parse("AA1").unwrap();
assert_eq!(r.col, 26);
assert_eq!(r.row, 0);
}
#[test]
fn parse_xfd1048576() {
let r = CellRef::parse("XFD1048576").unwrap();
assert_eq!(r.col, 16383);
assert_eq!(r.row, 1048575);
}
#[test]
fn col_name_round_trip() {
assert_eq!(CellRef::col_name(0), "A");
assert_eq!(CellRef::col_name(25), "Z");
assert_eq!(CellRef::col_name(26), "AA");
assert_eq!(CellRef::col_name(16383), "XFD");
}
#[test]
fn display_round_trip() {
let r = CellRef { col: 0, row: 0 };
assert_eq!(r.to_string(), "A1");
let r = CellRef { col: 26, row: 99 };
assert_eq!(r.to_string(), "AA100");
}
#[test]
fn parse_col_values() {
assert_eq!(CellRef::parse_col("A"), Some(0));
assert_eq!(CellRef::parse_col("Z"), Some(25));
assert_eq!(CellRef::parse_col("AA"), Some(26));
assert_eq!(CellRef::parse_col("AZ"), Some(51));
assert_eq!(CellRef::parse_col("BA"), Some(52));
}
#[test]
fn parse_invalid() {
assert!(CellRef::parse("").is_none());
assert!(CellRef::parse("1").is_none());
assert!(CellRef::parse("A").is_none());
assert!(CellRef::parse("A0").is_none());
}
}