use crate::{AsPaletteColor, Result, XmlWritable, XmlWriter}; use indexmap::{indexmap, IndexMap, IndexSet}; use rgb::RGB8; use std::cell::RefCell; use std::rc::Rc; #[derive(Default, Debug)] struct Inner { borders: IndexSet, } #[derive(Clone, Debug)] pub(crate) struct Borders { inner: Rc>, } impl Borders { pub fn create_or_get_index( &mut self, border: Option<&CellBorder>, ) -> Option { let mut inner = self.inner.borrow_mut(); border.map(|f| inner.borders.insert_full(f.clone()).0) } } impl Default for Borders { fn default() -> Self { let mut inner = Inner::default(); inner.borders.insert(CellBorder::default()); Borders { inner: Rc::new(RefCell::new(inner)), } } } impl XmlWritable for Borders { fn write_xml(&self, w: &mut W) -> Result<()> { self.inner.borrow().write_xml(w) } } impl XmlWritable for Inner { fn write_xml(&self, w: &mut W) -> Result<()> { if !self.borders.is_empty() { let tag = "borders"; let attrs = indexmap! { "count" => format!("{}", self.borders.len()), }; w.start_tag_with_attrs(tag, attrs)?; for border in self.borders.iter() { border.write_xml(w)?; } w.end_tag(tag)?; } Ok(()) } } #[derive(Default, Clone, Hash, PartialEq, Eq, Debug)] pub struct CellBorder { pub left: Option, pub right: Option, pub top: Option, pub bottom: Option, pub diagonal: Option, } impl CellBorder { pub fn set_around(&mut self, border: Option) { self.left = border; self.right = border; self.top = border; self.bottom = border; } } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] pub struct DiagonalBorder { pub diagonal_type: DiagonalBorderType, pub style: BorderStyle, pub color: Option, } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] pub struct Border { pub style: BorderStyle, /// If this is None, the color will be set to 'auto'. pub color: Option, } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] pub enum DiagonalBorderType { Up, Down, UpDown, } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] pub enum BorderStyle { /// Can be used to override a cell border if e.g. a row or column /// border was set. None, Thin, Medium, Dashed, Dotted, Thick, Double, Hair, MediumDashed, DashDot, MediumDashDot, DashDotDot, MediumDashDotDot, SlantDashDot, } impl AsRef for BorderStyle { fn as_ref(&self) -> &str { match self { BorderStyle::None => "none", BorderStyle::Thin => "thin", BorderStyle::Medium => "medium", BorderStyle::Dashed => "dashed", BorderStyle::Dotted => "dotted", BorderStyle::Thick => "thick", BorderStyle::Double => "dobule", BorderStyle::Hair => "hair", BorderStyle::MediumDashed => "mediumDashed", BorderStyle::DashDot => "dashDot", BorderStyle::MediumDashDot => "mediumDashDot", BorderStyle::DashDotDot => "dashDotDot", BorderStyle::MediumDashDotDot => "mediumDashDotDot", BorderStyle::SlantDashDot => "slantDashDot", } } } impl XmlWritable for CellBorder { fn write_xml(&self, w: &mut W) -> Result<()> { let tag = "border"; let attrs = if let Some(ref border) = self.diagonal { match border.diagonal_type { DiagonalBorderType::Up => { indexmap! {"diagonalUp" => "1"} } DiagonalBorderType::Down => { indexmap! {"diagonalDown" => "1"} } DiagonalBorderType::UpDown => { indexmap! { "diagonalUp" => "1", "diagonalDown" => "1", } } } } else { indexmap! {} }; w.start_tag_with_attrs(tag, attrs)?; self.write_left(w)?; self.write_right(w)?; self.write_top(w)?; self.write_bottom(w)?; self.write_diagonal(w)?; w.end_tag(tag)?; Ok(()) } } impl CellBorder { fn write_left(&self, w: &mut W) -> Result<()> { Self::write_optional_border(w, "left", self.left.as_ref()) } fn write_right(&self, w: &mut W) -> Result<()> { Self::write_optional_border(w, "right", self.right.as_ref()) } fn write_top(&self, w: &mut W) -> Result<()> { Self::write_optional_border(w, "top", self.top.as_ref()) } fn write_bottom(&self, w: &mut W) -> Result<()> { Self::write_optional_border(w, "bottom", self.bottom.as_ref()) } fn write_diagonal(&self, w: &mut W) -> Result<()> { Self::write_optional_border( w, "diagonal", self.diagonal .map(|d| Border { style: d.style, color: d.color, }) .as_ref(), ) } fn write_optional_border( w: &mut W, name: &str, border: Option<&Border>, ) -> Result<()> { if let Some(b) = border { b.write_xml(w, name) } else { w.empty_tag(name) } } } impl Border { fn write_xml( &self, w: &mut W, name: &str, ) -> Result<()> { let attrs = indexmap! { "style" => self.style.as_ref() }; w.start_tag_with_attrs(name, attrs)?; { let mut attrs = IndexMap::new(); if let Some(color) = self.color { attrs.insert("rgb", color.as_palette_color()); } else { attrs.insert("auto", "1".to_string()); } w.empty_tag_with_attrs("color", attrs)?; } w.end_tag(name)?; Ok(()) } }