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<CellBorder>,
}
#[derive(Clone, Debug)]
pub(crate) struct Borders {
inner: Rc<RefCell<Inner>>,
}
impl Borders {
pub fn create_or_get_index(
&mut self,
border: Option<&CellBorder>,
) -> Option<usize> {
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<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
self.inner.borrow().write_xml(w)
}
}
impl XmlWritable for Inner {
fn write_xml<W: XmlWriter>(&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<Border>,
pub right: Option<Border>,
pub top: Option<Border>,
pub bottom: Option<Border>,
pub diagonal: Option<DiagonalBorder>,
}
impl CellBorder {
pub fn set_around(&mut self, border: Option<Border>) {
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<RGB8>,
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
pub struct Border {
pub style: BorderStyle,
pub color: Option<RGB8>,
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
pub enum DiagonalBorderType {
Up,
Down,
UpDown,
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
pub enum BorderStyle {
None,
Thin,
Medium,
Dashed,
Dotted,
Thick,
Double,
Hair,
MediumDashed,
DashDot,
MediumDashDot,
DashDotDot,
MediumDashDotDot,
SlantDashDot,
}
impl AsRef<str> 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<W: XmlWriter>(&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<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
Self::write_optional_border(w, "left", self.left.as_ref())
}
fn write_right<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
Self::write_optional_border(w, "right", self.right.as_ref())
}
fn write_top<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
Self::write_optional_border(w, "top", self.top.as_ref())
}
fn write_bottom<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
Self::write_optional_border(w, "bottom", self.bottom.as_ref())
}
fn write_diagonal<W: XmlWriter>(&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: XmlWriter>(
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<W: XmlWriter>(
&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(())
}
}