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 {
self.a1_key().as_str().to_owned()
}
pub(crate) fn a1_key(&self) -> A1Key {
let mut key = A1Key {
buf: [0; A1_KEY_CAPACITY],
len: 0,
};
let mut letters = [0u8; MAX_COLUMN_LETTERS];
let mut n = 0;
let mut column = self.column;
while column > 0 {
letters[n] = b'A' + ((column - 1) % 26) as u8;
n += 1;
column = (column - 1) / 26;
}
while n > 0 {
n -= 1;
key.push(letters[n]);
}
let mut digits = [0u8; MAX_ROW_DIGITS];
let mut d = 0;
let mut row = self.row;
while row > 0 {
digits[d] = b'0' + (row % 10) as u8;
d += 1;
row /= 10;
}
while d > 0 {
d -= 1;
key.push(digits[d]);
}
key
}
}
const MAX_COLUMN_LETTERS: usize = 3;
const MAX_ROW_DIGITS: usize = 8;
const A1_KEY_CAPACITY: usize = MAX_COLUMN_LETTERS + MAX_ROW_DIGITS;
pub(crate) struct A1Key {
buf: [u8; A1_KEY_CAPACITY],
len: usize,
}
impl A1Key {
fn push(&mut self, byte: u8) {
self.buf[self.len] = byte;
self.len += 1;
}
pub(crate) fn as_str(&self) -> &str {
std::str::from_utf8(&self.buf[..self.len]).expect("A1 keys are ASCII")
}
}
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)
}