use crate::{error, Result}; use snafu::OptionExt; use std::cmp::{max, min}; use std::convert::TryFrom; use std::str::FromStr; pub(crate) const ROW_MAX: u32 = 0x100000; pub(crate) const COL_MAX: u16 = 0x4000; #[derive(Debug, Clone, Copy)] pub enum ReferenceMode { Relative, Absolute, } impl ReferenceMode { pub fn sign(&self) -> String { match self { ReferenceMode::Relative => "".to_string(), ReferenceMode::Absolute => "$".to_string(), } } } impl Default for ReferenceMode { fn default() -> Self { ReferenceMode::Relative } } // TODO: remove this once we know all types. pub(crate) struct UnknownData; pub(crate) struct Cell { pub(crate) format: Option, pub(crate) data: Data, } impl Cell { pub fn new_number(value: f64, format: Option) -> Self { Cell { format, data: Data::Number(value), } } pub fn new_string(string_index: usize, format: Option) -> Self { Cell { format, data: Data::String(string_index), } } pub fn new_formula( formula: String, value: f64, format: Option, ) -> Self { Cell { format, data: Data::Formula(formula, value), } } pub fn new_blank(format: Option) -> Self { Cell { format, data: Data::Blank, } } } pub(crate) enum Data { Number(f64), String(usize), InlineString(String), InlineRichString(UnknownData), Formula(String, f64), ArrayFormula(UnknownData), Blank, Boolean(UnknownData), HyperlinkUrl(UnknownData), HyperlinkInternal(UnknownData), HyperlinkExternal(UnknownData), } pub trait AsRangeString { type Mode: Default; fn as_range_string_mode(&self, mode: Self::Mode) -> String; fn as_range_string(&self) -> String { self.as_range_string_mode(Self::Mode::default()) } } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Index { pub row: Row, pub col: Col, } impl Index { pub fn new(row: Row, col: Col) -> Self { Index { row, col } } pub fn saturating_down(mut self, rows: u32) -> Self { self.row = self.row.saturating_add(rows); self } pub fn saturating_up(mut self, rows: u32) -> Self { self.row = self.row.saturating_sub(rows); self } pub fn saturating_right(mut self, cols: u16) -> Self { self.col = self.col.saturating_add(cols); self } pub fn saturating_left(mut self, cols: u16) -> Self { self.col = self.col.saturating_sub(cols); self } } impl AsRangeString for Index { type Mode = (ReferenceMode, ReferenceMode); fn as_range_string_mode(&self, mode: Self::Mode) -> String { let (row_mode, col_mode) = mode; let Index { row, col } = *self; format!( "{}{}", col.as_range_string_mode(col_mode), row.as_range_string_mode(row_mode), ) } } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct IndexRange { pub row_range: RowRange, pub col_range: ColRange, } impl IndexRange { pub fn new_from_ranges( row_range: RowRange, col_range: ColRange, ) -> Self { IndexRange { row_range, col_range, } } pub fn new_indexes(first: Index, last: Index) -> IndexRange { IndexRange { row_range: RowRange::build(first.row, last.row), col_range: ColRange::build(first.col, last.col), } } pub fn new_row_col_row_col( row1: R1, col1: C1, row2: R2, col2: C2, ) -> Result where Row: TryFrom, Col: TryFrom, Row: TryFrom, Col: TryFrom, { Ok(IndexRange { row_range: RowRange::build( Row::try_from(row1)?, Row::try_from(row2)?, ), col_range: ColRange::build( Col::try_from(col1)?, Col::try_from(col2)?, ), }) } pub fn extend_to(&mut self, r: R) -> &mut Self where IndexRange: From, { let other = IndexRange::from(r); self.row_range.extend_to(other.row_range); self.col_range.extend_to(other.col_range); self } pub fn extend_to_col(&mut self, col: Col) -> &mut Self { self.col_range.extend_to(ColRange::build(col, col)); self } pub fn extend_to_row(&mut self, row: Row) -> &mut Self { self.row_range.extend_to(RowRange::build(row, row)); self } pub fn top_left(&self) -> Index { Index { row: self.row_range.first, col: self.col_range.first, } } pub fn top_right(&self) -> Index { Index { row: self.row_range.first, col: self.col_range.last, } } pub fn bottom_left(&self) -> Index { Index { row: self.row_range.last, col: self.col_range.first, } } pub fn bottom_right(&self) -> Index { Index { row: self.row_range.last, col: self.col_range.last, } } } impl From for IndexRange { fn from(index: Index) -> IndexRange { IndexRange { row_range: index.row.into(), col_range: index.col.into(), } } } impl AsRangeString for IndexRange { type Mode = (ReferenceMode, ReferenceMode); fn as_range_string_mode(&self, mode: Self::Mode) -> String { let IndexRange { row_range, col_range, } = self; let mut s = Index { row: row_range.first, col: col_range.first, } .as_range_string_mode(mode); // If the start and end cells are the same just return a single cell. if row_range.first != row_range.last || col_range.first != col_range.last { s.push(':'); s.push_str( &Index { row: row_range.last, col: col_range.last, } .as_range_string_mode(mode), ); } s } } pub fn index(row_num: u32, col_num: u16) -> Result { Ok(Index { row: row(row_num)?, col: col(col_num)?, }) } /// Convert an Excel `A1` type cell string into an Index struct. pub fn cell(s: &str) -> Result { let first_digit = s.find(|c: char| c.is_ascii_digit()).context( error::ParseCellError { string: s.to_string(), }, )?; let (col, row) = s.split_at(first_digit); match ( col.find(|c: char| !c.is_ascii_alphabetic()), row.find(|c: char| !c.is_ascii_digit()), ) { (None, None) => { let row = u32::from_str(row).ok().context( error::ParseCellError { string: s.to_string(), }, )? - 1; let row = Row::try_from(row)?; Ok(Index { row, col: Col::parse_from(col)?, }) } _ => error::ParseCellError { string: s.to_string(), } .fail(), } } pub fn row(number: R) -> Result where Row: TryFrom, { Row::try_from(number) } pub fn col(number: C) -> Result where Col: TryFrom, { Col::try_from(number) } pub fn row_range(first: R, last: R) -> Result where Row: TryFrom, { let first = row(first)?; let last = row(last)?; Ok(RowRange::build(first, last)) } pub fn col_range(first: C, last: C) -> Result where Col: TryFrom, { let first = col(first)?; let last = col(last)?; Ok(ColRange::build(first, last)) } pub fn range( first_row: R, last_row: R, first_col: C, last_col: C, ) -> Result where Row: TryFrom, Col: TryFrom, { Ok(IndexRange { row_range: row_range(first_row, last_row)?, col_range: col_range(first_col, last_col)?, }) } #[derive( Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd, )] pub struct Row { number: u32, } impl Row { /// Add a number of rows, saturate the value to the highest /// row number available. pub fn saturating_add(mut self, num: u32) -> Self { self.number = self.number.saturating_add(num); if self.number >= ROW_MAX { self.number = ROW_MAX - 1; } self } /// Subtract a number of rows, saturate the value to the lowest /// row number available. pub fn saturating_sub(mut self, num: u32) -> Self { self.number = self.number.saturating_sub(num); self } /// Add a number of rows, checking the value range. pub fn checked_add(mut self, num: u32) -> Option { match self.number.checked_add(num) { Some(n) if n < ROW_MAX => { self.number = n; Some(self) } Some(_) | None => None, } } /// Subtract a number of rows, saturate the value to the lowest /// row number available. pub fn checked_sub(mut self, num: u32) -> Option { match self.number.checked_sub(num) { Some(n) => { self.number = n; Some(self) } None => None, } } } impl AsRangeString for Row { type Mode = ReferenceMode; fn as_range_string_mode(&self, mode: Self::Mode) -> String { format!("{}{}", mode.sign(), self.number + 1) } } impl TryFrom for Row { type Error = error::Error; fn try_from(number: u32) -> Result { if number < ROW_MAX { Ok(Row { number }) } else { error::RowNumberOutOfRange { number, min: 0u32, max: ROW_MAX - 1, } .fail() } } } impl Into for Row { fn into(self) -> u32 { self.number } } #[derive( Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, )] pub struct Col { number: u16, } impl Col { /// Add a number of cols, saturate the value to the highest /// col number available. pub fn saturating_add(mut self, num: u16) -> Self { self.number = self.number.saturating_add(num); if self.number >= COL_MAX { self.number = COL_MAX - 1; } self } /// Subtract a number of cols, saturate the value to the lowest /// col number available. pub fn saturating_sub(mut self, num: u16) -> Self { self.number = self.number.saturating_sub(num); self } /// Add a number of cols, checking the value range. pub fn checked_add(mut self, num: u16) -> Option { match self.number.checked_add(num) { Some(n) if n < COL_MAX => { self.number = n; Some(self) } Some(_) | None => None, } } /// Subtract a number of cols, saturate the value to the lowest /// row number available. pub fn checked_sub(mut self, num: u16) -> Option { match self.number.checked_sub(num) { Some(n) => { self.number = n; Some(self) } None => None, } } } impl Col { fn parse_from(s: &str) -> Result { if s.is_empty() { error::ParseColError { string: s.to_string(), } .fail()?; } if s.find(|c: char| !c.is_ascii_alphabetic()).is_some() { error::ParseColError { string: s.to_string(), } .fail()?; } let mut number: u16 = 0; for c in s.to_uppercase().chars() { number *= 26; let difference = c as u32 - 'A' as u32; number += difference as u16; } Ok(Col { number }) } } impl AsRangeString for Col { type Mode = ReferenceMode; fn as_range_string_mode(&self, mode: Self::Mode) -> String { let mut s = mode.sign(); let mut number = self.number as u32 + 1; let a = "A".as_bytes()[0] as u32; while number > 0 { let mut remainder = number % 26; if remainder == 0 { remainder = 26; } s.insert(0, char::try_from(a + remainder - 1).unwrap()); number = (number - 1) / 26; } s } } impl TryFrom for Col { type Error = error::Error; fn try_from(number: u16) -> Result { if number < COL_MAX { Ok(Col { number }) } else { error::ColNumberOutOfRange { number, min: 0u16, max: COL_MAX - 1, } .fail() } } } impl Into for Col { fn into(self) -> u16 { self.number } } #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)] pub struct RowRange { first: Row, last: Row, } impl RowRange { pub fn build(a: Row, b: Row) -> Self { let (first, last) = if a > b { (b, a) } else { (a, b) }; RowRange { first, last } } pub fn extend_to(&mut self, r: R) -> &mut Self where RowRange: From, { let other = RowRange::from(r); self.first = min(self.first, other.first); self.last = max(self.last, other.last); self } pub fn first(&self) -> Row { self.first } pub fn last(&self) -> Row { self.last } pub fn iter(&self) -> RowRangeIter { RowRangeIter { current: self.first, last: self.last, } } } impl IntoIterator for RowRange { type Item = Row; type IntoIter = RowRangeIter; fn into_iter(self) -> Self::IntoIter { RowRangeIter { current: self.first, last: self.last, } } } impl From for RowRange { fn from(row: Row) -> RowRange { RowRange { first: row, last: row, } } } pub struct RowRangeIter { current: Row, last: Row, } impl Iterator for RowRangeIter { type Item = Row; fn next(&mut self) -> Option { if self.current > self.last { None } else { let current = self.current; self.current.number += 1; Some(current) } } } #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)] pub struct ColRange { first: Col, last: Col, } impl ColRange { pub fn build(a: Col, b: Col) -> Self { let (first, last) = if a > b { (b, a) } else { (a, b) }; ColRange { first, last } } pub fn extend_to(&mut self, c: C) -> &mut Self where ColRange: From, { let other = ColRange::from(c); self.first = min(self.first, other.first); self.last = max(self.last, other.last); self } pub fn first(&self) -> Col { self.first } pub fn last(&self) -> Col { self.last } pub fn iter(&self) -> ColRangeIter { ColRangeIter { current: self.first, last: self.last, } } } impl IntoIterator for ColRange { type Item = Col; type IntoIter = ColRangeIter; fn into_iter(self) -> Self::IntoIter { ColRangeIter { current: self.first, last: self.last, } } } impl From for ColRange { fn from(col: Col) -> ColRange { ColRange { first: col, last: col, } } } pub struct ColRangeIter { current: Col, last: Col, } impl Iterator for ColRangeIter { type Item = Col; fn next(&mut self) -> Option { if self.current > self.last { None } else { let current = self.current; self.current.number += 1; Some(current) } } }