use std::fmt;
use std::str::FromStr;
use crate::ValidationError;
pub const EXCEL_MAX_ROWS: u32 = 1_048_576;
pub const EXCEL_MAX_COLUMNS: u32 = 16_384;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Row(u32);
impl Row {
pub fn new(value: u32) -> Result<Self, ValidationError> {
if !(1..=EXCEL_MAX_ROWS).contains(&value) {
return Err(ValidationError::RowOutOfRange { value });
}
Ok(Self(value))
}
pub const fn get(self) -> u32 {
self.0
}
}
impl TryFrom<u32> for Row {
type Error = ValidationError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Column(u32);
impl Column {
pub fn new(value: u32) -> Result<Self, ValidationError> {
if !(1..=EXCEL_MAX_COLUMNS).contains(&value) {
return Err(ValidationError::ColumnOutOfRange { value });
}
Ok(Self(value))
}
pub const fn get(self) -> u32 {
self.0
}
}
impl TryFrom<u32> for Column {
type Error = ValidationError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CellAddress {
row: Row,
column: Column,
}
impl CellAddress {
pub const fn new(row: Row, column: Column) -> Self {
Self { row, column }
}
pub fn from_indices(row: u32, column: u32) -> Result<Self, ValidationError> {
Ok(Self::new(Row::new(row)?, Column::new(column)?))
}
pub fn from_a1(value: &str) -> Result<Self, ValidationError> {
let bytes = value.as_bytes();
let Some(row_start) = bytes.iter().position(u8::is_ascii_digit) else {
return Err(ValidationError::CellAddressInvalid);
};
if row_start == 0
|| !bytes[..row_start].iter().all(u8::is_ascii_alphabetic)
|| !bytes[row_start..].iter().all(u8::is_ascii_digit)
{
return Err(ValidationError::CellAddressInvalid);
}
let mut column = 0_u32;
for byte in &bytes[..row_start] {
column = column * 26 + u32::from(byte.to_ascii_uppercase() - b'A' + 1);
if column > EXCEL_MAX_COLUMNS {
return Err(ValidationError::ColumnOutOfRange { value: column });
}
}
let mut row = 0_u32;
for byte in &bytes[row_start..] {
row = row * 10 + u32::from(*byte - b'0');
if row > EXCEL_MAX_ROWS {
return Err(ValidationError::RowOutOfRange { value: row });
}
}
Self::from_indices(row, column)
}
pub const fn row(self) -> Row {
self.row
}
pub const fn column(self) -> Column {
self.column
}
}
impl FromStr for CellAddress {
type Err = ValidationError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Self::from_a1(value)
}
}
impl fmt::Display for CellAddress {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&column_label(self.column.get()))?;
write!(formatter, "{}", self.row.get())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CellRange {
start: CellAddress,
end: CellAddress,
}
impl CellRange {
pub fn new(start: CellAddress, end: CellAddress) -> Result<Self, ValidationError> {
if start.row > end.row || start.column > end.column {
return Err(ValidationError::RangeStartAfterEnd);
}
Ok(Self { start, end })
}
pub(crate) const fn from_ordered(start: CellAddress, end: CellAddress) -> Self {
Self { start, end }
}
pub const fn start(self) -> CellAddress {
self.start
}
pub const fn end(self) -> CellAddress {
self.end
}
pub const fn height(self) -> u32 {
self.end.row.get() - self.start.row.get() + 1
}
pub const fn width(self) -> u32 {
self.end.column.get() - self.start.column.get() + 1
}
pub const fn contains(self, address: CellAddress) -> bool {
address.row.get() >= self.start.row.get()
&& address.row.get() <= self.end.row.get()
&& address.column.get() >= self.start.column.get()
&& address.column.get() <= self.end.column.get()
}
}
fn column_label(mut column: u32) -> String {
let mut reversed = Vec::with_capacity(3);
while column > 0 {
column -= 1;
reversed.push(b'A' + (column % 26) as u8);
column /= 26;
}
reversed.reverse();
reversed.into_iter().map(char::from).collect()
}