use crate::cell::{Cell, Value};
use crate::error::Result;
use crate::reference::{CellRef, RangeRef};
use crate::style::StyleId;
use std::collections::{BTreeMap, HashSet};
#[derive(Debug, Clone)]
pub struct Worksheet {
pub(crate) name: String,
pub(crate) sheet_id: u32,
pub(crate) cells: BTreeMap<(u32, u32), Cell>,
pub(crate) row_heights: BTreeMap<u32, f64>,
pub(crate) col_widths: BTreeMap<u32, f64>,
pub(crate) hidden_rows: HashSet<u32>,
pub(crate) hidden_cols: HashSet<u32>,
pub(crate) merged_cells: Vec<RangeRef>,
pub(crate) tab_color: Option<String>,
}
impl Worksheet {
pub(crate) fn new<S: Into<String>>(name: S, sheet_id: u32) -> Self {
Self {
name: name.into(),
sheet_id,
cells: BTreeMap::new(),
row_heights: BTreeMap::new(),
col_widths: BTreeMap::new(),
hidden_rows: HashSet::new(),
hidden_cols: HashSet::new(),
merged_cells: Vec::new(),
tab_color: None,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn set_name<S: Into<String>>(&mut self, name: S) {
self.name = name.into();
}
pub fn sheet_id(&self) -> u32 {
self.sheet_id
}
pub fn cell(&self, reference: &str) -> &Cell {
static EMPTY: Cell = Cell::empty();
match CellRef::parse(reference) {
Ok(r) => self.cells.get(&(r.row, r.col)).unwrap_or(&EMPTY),
Err(_) => &EMPTY,
}
}
pub fn cell_mut(&mut self, reference: &str) -> Result<&mut Cell> {
let r = CellRef::parse(reference)?;
Ok(self.cells.entry((r.row, r.col)).or_default())
}
pub fn cell_at(&self, row: u32, col: u32) -> &Cell {
static EMPTY: Cell = Cell::empty();
self.cells.get(&(row, col)).unwrap_or(&EMPTY)
}
pub fn cell_at_mut(&mut self, row: u32, col: u32) -> &mut Cell {
self.cells.entry((row, col)).or_default()
}
pub fn set_cell<V: Into<Value>>(&mut self, reference: &str, value: V) -> Result<()> {
let r = CellRef::parse(reference)?;
self.cells.insert((r.row, r.col), Cell::new(value));
Ok(())
}
pub fn set_cell_at<V: Into<Value>>(&mut self, row: u32, col: u32, value: V) {
self.cells.insert((row, col), Cell::new(value));
}
pub fn merge_cells(&mut self, range: &str) -> Result<()> {
let range = RangeRef::parse(range)?;
self.merged_cells.push(range.normalized());
Ok(())
}
pub fn merged_cells(&self) -> &[RangeRef] {
&self.merged_cells
}
pub fn set_row_height(&mut self, row: u32, height: f64) {
if row == 0 {
return;
}
self.row_heights.insert(row, height);
}
pub fn row_height(&self, row: u32) -> Option<f64> {
self.row_heights.get(&row).copied()
}
pub fn hide_row(&mut self, row: u32) {
if row > 0 {
self.hidden_rows.insert(row);
}
}
pub fn show_row(&mut self, row: u32) {
self.hidden_rows.remove(&row);
}
pub fn is_row_hidden(&self, row: u32) -> bool {
self.hidden_rows.contains(&row)
}
pub fn set_column_width(&mut self, col: &str, width: f64) -> Result<()> {
let col_idx = crate::reference::letters_to_col(col)?;
self.col_widths.insert(col_idx, width);
Ok(())
}
pub fn column_width(&self, col: u32) -> Option<f64> {
self.col_widths.get(&col).copied()
}
pub fn hide_column(&mut self, col: u32) {
if col > 0 {
self.hidden_cols.insert(col);
}
}
pub fn show_column(&mut self, col: u32) {
self.hidden_cols.remove(&col);
}
pub fn is_column_hidden(&self, col: u32) -> bool {
self.hidden_cols.contains(&col)
}
pub fn set_tab_color<S: Into<String>>(&mut self, color: S) {
self.tab_color = Some(color.into());
}
pub fn tab_color(&self) -> Option<&str> {
self.tab_color.as_deref()
}
pub fn dimension(&self) -> Option<RangeRef> {
if self.cells.is_empty() {
return None;
}
let mut min_row = u32::MAX;
let mut min_col = u32::MAX;
let mut max_row = 0;
let mut max_col = 0;
for (row, col) in self.cells.keys() {
min_row = min_row.min(*row);
min_col = min_col.min(*col);
max_row = max_row.max(*row);
max_col = max_col.max(*col);
}
Some(
RangeRef::new(
CellRef::new(min_row, min_col).ok()?,
CellRef::new(max_row, max_col).ok()?,
)
.normalized(),
)
}
pub fn iter_cells(&self) -> impl Iterator<Item = (u32, u32, &Cell)> {
self.cells
.iter()
.map(|((row, col), cell)| (*row, *col, cell))
}
pub fn clear(&mut self) {
self.cells.clear();
}
pub fn delete_cell(&mut self, reference: &str) -> Result<()> {
let r = CellRef::parse(reference)?;
self.cells.remove(&(r.row, r.col));
Ok(())
}
pub fn set_cell_style(&mut self, reference: &str, style_id: StyleId) -> Result<()> {
let r = CellRef::parse(reference)?;
self.cells
.entry((r.row, r.col))
.or_default()
.set_style_id(style_id);
Ok(())
}
}
impl std::ops::Index<&str> for Worksheet {
type Output = Cell;
fn index(&self, reference: &str) -> &Self::Output {
self.cell(reference)
}
}
impl std::ops::IndexMut<&str> for Worksheet {
fn index_mut(&mut self, reference: &str) -> &mut Self::Output {
let r = CellRef::parse(reference).expect("invalid cell reference");
self.cells.entry((r.row, r.col)).or_default()
}
}