use crate::limits::{MAX_COLUMN, MAX_ROW};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Address {
pub row: u32,
pub column: u32,
}
impl Address {
pub fn new(row: u32, column: u32) -> Option<Self> {
if row == 0 || row > MAX_ROW || column == 0 || column > MAX_COLUMN {
return None;
}
Some(Self { row, column })
}
pub fn from_a1(key: &str) -> Option<Self> {
parse_a1(key)
}
pub fn to_a1(&self) -> String {
let mut s = column_to_letters(self.column);
s.push_str(&self.row.to_string());
s
}
}
pub fn parse_a1(key: &str) -> Option<Address> {
let bytes = key.as_bytes();
let mut i = 0;
let mut column: u32 = 0;
while i < bytes.len() && bytes[i].is_ascii_uppercase() {
if i >= 3 {
return None; }
column = column * 26 + (bytes[i] - b'A' + 1) as u32;
i += 1;
}
if i == 0 {
return None; }
let digits = &bytes[i..];
if digits.is_empty() || digits.len() > 8 {
return None;
}
if digits[0] == b'0' {
return None; }
let mut row: u32 = 0;
for &b in digits {
if !b.is_ascii_digit() {
return None;
}
row = row.checked_mul(10)?.checked_add((b - b'0') as u32)?;
}
Address::new(row, column)
}
pub(crate) fn column_to_letters(mut column: u32) -> String {
let mut buf = [0u8; 3]; let mut len = 0;
while column > 0 {
let rem = ((column - 1) % 26) as u8;
buf[len] = b'A' + rem;
len += 1;
column = (column - 1) / 26;
}
buf[..len].reverse();
String::from_utf8(buf[..len].to_vec()).expect("ASCII letters are valid UTF-8")
}