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
}
}
pub(crate) struct UnknownData;
pub(crate) struct Cell {
pub(crate) format: Option<usize>,
pub(crate) data: Data,
}
impl Cell {
pub fn new_number(value: f64, format: Option<usize>) -> Self {
Cell {
format,
data: Data::Number(value),
}
}
pub fn new_string(string_index: usize, format: Option<usize>) -> Self {
Cell {
format,
data: Data::String(string_index),
}
}
pub fn new_formula(
formula: String,
value: f64,
format: Option<usize>,
) -> Self {
Cell {
format,
data: Data::Formula(formula, value),
}
}
pub fn new_blank(format: Option<usize>) -> 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<R1, C1, R2, C2>(
row1: R1,
col1: C1,
row2: R2,
col2: C2,
) -> Result<IndexRange>
where
Row: TryFrom<R1, Error = error::Error>,
Col: TryFrom<C1, Error = error::Error>,
Row: TryFrom<R2, Error = error::Error>,
Col: TryFrom<C2, Error = error::Error>,
{
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<R>(&mut self, r: R) -> &mut Self
where
IndexRange: From<R>,
{
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<Index> 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 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<Index> {
Ok(Index {
row: row(row_num)?,
col: col(col_num)?,
})
}
pub fn cell(s: &str) -> Result<Index> {
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<R>(number: R) -> Result<Row>
where
Row: TryFrom<R, Error = error::Error>,
{
Row::try_from(number)
}
pub fn col<C>(number: C) -> Result<Col>
where
Col: TryFrom<C, Error = error::Error>,
{
Col::try_from(number)
}
pub fn row_range<R>(first: R, last: R) -> Result<RowRange>
where
Row: TryFrom<R, Error = error::Error>,
{
let first = row(first)?;
let last = row(last)?;
Ok(RowRange::build(first, last))
}
pub fn col_range<C>(first: C, last: C) -> Result<ColRange>
where
Col: TryFrom<C, Error = error::Error>,
{
let first = col(first)?;
let last = col(last)?;
Ok(ColRange::build(first, last))
}
pub fn range<R, C>(
first_row: R,
last_row: R,
first_col: C,
last_col: C,
) -> Result<IndexRange>
where
Row: TryFrom<R, Error = error::Error>,
Col: TryFrom<C, Error = error::Error>,
{
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 {
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
}
pub fn saturating_sub(mut self, num: u32) -> Self {
self.number = self.number.saturating_sub(num);
self
}
pub fn checked_add(mut self, num: u32) -> Option<Self> {
match self.number.checked_add(num) {
Some(n) if n < ROW_MAX => {
self.number = n;
Some(self)
}
Some(_) | None => None,
}
}
pub fn checked_sub(mut self, num: u32) -> Option<Self> {
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<u32> for Row {
type Error = error::Error;
fn try_from(number: u32) -> Result<Self> {
if number < ROW_MAX {
Ok(Row { number })
} else {
error::RowNumberOutOfRange {
number,
min: 0u32,
max: ROW_MAX - 1,
}
.fail()
}
}
}
impl Into<u32> 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 {
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
}
pub fn saturating_sub(mut self, num: u16) -> Self {
self.number = self.number.saturating_sub(num);
self
}
pub fn checked_add(mut self, num: u16) -> Option<Self> {
match self.number.checked_add(num) {
Some(n) if n < COL_MAX => {
self.number = n;
Some(self)
}
Some(_) | None => None,
}
}
pub fn checked_sub(mut self, num: u16) -> Option<Self> {
match self.number.checked_sub(num) {
Some(n) => {
self.number = n;
Some(self)
}
None => None,
}
}
}
impl Col {
fn parse_from(s: &str) -> Result<Col> {
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<u16> for Col {
type Error = error::Error;
fn try_from(number: u16) -> Result<Self> {
if number < COL_MAX {
Ok(Col { number })
} else {
error::ColNumberOutOfRange {
number,
min: 0u16,
max: COL_MAX - 1,
}
.fail()
}
}
}
impl Into<u16> 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<R>(&mut self, r: R) -> &mut Self
where
RowRange: From<R>,
{
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<Row> 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<Self::Item> {
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<C>(&mut self, c: C) -> &mut Self
where
ColRange: From<C>,
{
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<Col> 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<Self::Item> {
if self.current > self.last {
None
} else {
let current = self.current;
self.current.number += 1;
Some(current)
}
}
}