use indexmap::IndexMap;
use std::sync::Arc;
mod alignment;
mod border;
mod fill;
mod font;
mod numfmt;
pub use alignment::{Alignment, HorizontalAlignment, VerticalAlignment};
pub use border::{Border, BorderStyle, DiagonalBorder, Side};
pub use fill::{Fill, PatternFill, PatternType};
pub use font::{Font, UnderlineStyle};
pub use numfmt::NumberFormat;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StyleId(pub usize);
impl StyleId {
pub fn index(&self) -> usize {
self.0
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct Style {
pub(crate) font: Option<Font>,
pub(crate) fill: Option<Fill>,
pub(crate) border: Option<Border>,
pub(crate) alignment: Option<Alignment>,
pub(crate) number_format: Option<NumberFormat>,
pub(crate) protection: Option<Protection>,
}
impl Style {
pub fn new() -> Self {
Self::default()
}
pub fn font(mut self, font: Font) -> Self {
self.font = Some(font);
self
}
pub fn fill(mut self, fill: Fill) -> Self {
self.fill = Some(fill);
self
}
pub fn border(mut self, border: Border) -> Self {
self.border = Some(border);
self
}
pub fn alignment(mut self, alignment: Alignment) -> Self {
self.alignment = Some(alignment);
self
}
pub fn number_format(mut self, number_format: NumberFormat) -> Self {
self.number_format = Some(number_format);
self
}
pub fn protection(mut self, protection: Protection) -> Self {
self.protection = Some(protection);
self
}
pub fn solid_fill<S: Into<String>>(mut self, rgb: S) -> Self {
self.fill = Some(Fill::Pattern(PatternFill::solid(rgb)));
self
}
pub fn get_font(&self) -> Option<&Font> {
self.font.as_ref()
}
pub fn get_fill(&self) -> Option<&Fill> {
self.fill.as_ref()
}
pub fn get_border(&self) -> Option<&Border> {
self.border.as_ref()
}
pub fn get_alignment(&self) -> Option<&Alignment> {
self.alignment.as_ref()
}
pub fn get_number_format(&self) -> Option<&NumberFormat> {
self.number_format.as_ref()
}
pub fn get_protection(&self) -> Option<&Protection> {
self.protection.as_ref()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Protection {
pub locked: bool,
pub hidden: bool,
}
impl Protection {
pub fn new(locked: bool, hidden: bool) -> Self {
Self { locked, hidden }
}
}
#[derive(Debug, Clone, Default)]
pub struct StyleManager {
styles: IndexMap<Arc<Style>, StyleId>,
fonts: IndexMap<Font, usize>,
fills: IndexMap<Fill, usize>,
borders: IndexMap<Border, usize>,
number_formats: IndexMap<NumberFormat, usize>,
cell_xfs: Vec<CellXf>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct CellXf {
pub font_id: Option<usize>,
pub fill_id: Option<usize>,
pub border_id: Option<usize>,
pub num_fmt_id: Option<usize>,
pub alignment: Option<Alignment>,
pub protection: Option<Protection>,
}
impl StyleManager {
pub fn register(&mut self, style: Style) -> StyleId {
if let Some(&id) = self.styles.get(&Arc::new(style.clone())) {
return id;
}
let font_id = style.font.as_ref().map(|f| {
if let Some(&idx) = self.fonts.get(f) {
idx
} else {
let idx = self.fonts.len();
self.fonts.insert(f.clone(), idx);
idx
}
});
let fill_id = style.fill.as_ref().map(|fi| {
if let Some(&idx) = self.fills.get(fi) {
idx
} else {
let idx = self.fills.len();
self.fills.insert(fi.clone(), idx);
idx
}
});
let border_id = style.border.as_ref().map(|b| {
if let Some(&idx) = self.borders.get(b) {
idx
} else {
let idx = self.borders.len();
self.borders.insert(b.clone(), idx);
idx
}
});
let num_fmt_id = style.number_format.as_ref().map(|n| {
if let Some(&idx) = self.number_formats.get(n) {
idx
} else {
let idx = self.number_formats.len() + 164;
self.number_formats.insert(n.clone(), idx);
idx
}
});
let xf = CellXf {
font_id,
fill_id,
border_id,
num_fmt_id,
alignment: style.alignment.clone(),
protection: style.protection,
};
let _xf_id = self.cell_xfs.len();
self.cell_xfs.push(xf);
let style_id = StyleId(self.styles.len());
self.styles.insert(Arc::new(style), style_id);
style_id
}
pub fn get(&self, id: StyleId) -> Option<&Style> {
self.styles.get_index(id.0).map(|(k, _)| k.as_ref())
}
pub fn iter(&self) -> impl Iterator<Item = (StyleId, &Style)> {
self.styles
.iter()
.map(|(style, id)| (*id, style.as_ref()))
}
pub fn len(&self) -> usize {
self.styles.len()
}
pub fn is_empty(&self) -> bool {
self.styles.is_empty()
}
pub fn fonts(&self) -> &IndexMap<Font, usize> {
&self.fonts
}
pub fn fills(&self) -> &IndexMap<Fill, usize> {
&self.fills
}
pub fn borders(&self) -> &IndexMap<Border, usize> {
&self.borders
}
pub fn number_formats(&self) -> &IndexMap<NumberFormat, usize> {
&self.number_formats
}
pub(crate) fn cell_xfs(&self) -> &[CellXf] {
&self.cell_xfs
}
}