karo/
borders.rs

1use crate::{AsPaletteColor, Result, XmlWritable, XmlWriter};
2use indexmap::{indexmap, IndexMap, IndexSet};
3use rgb::RGB8;
4use std::cell::RefCell;
5use std::rc::Rc;
6
7#[derive(Default, Debug)]
8struct Inner {
9    borders: IndexSet<CellBorder>,
10}
11
12#[derive(Clone, Debug)]
13pub(crate) struct Borders {
14    inner: Rc<RefCell<Inner>>,
15}
16
17impl Borders {
18    pub fn create_or_get_index(
19        &mut self,
20        border: Option<&CellBorder>,
21    ) -> Option<usize> {
22        let mut inner = self.inner.borrow_mut();
23        border.map(|f| inner.borders.insert_full(f.clone()).0)
24    }
25}
26
27impl Default for Borders {
28    fn default() -> Self {
29        let mut inner = Inner::default();
30        inner.borders.insert(CellBorder::default());
31        Borders {
32            inner: Rc::new(RefCell::new(inner)),
33        }
34    }
35}
36
37impl XmlWritable for Borders {
38    fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
39        self.inner.borrow().write_xml(w)
40    }
41}
42
43impl XmlWritable for Inner {
44    fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
45        if !self.borders.is_empty() {
46            let tag = "borders";
47            let attrs = indexmap! {
48                "count" => format!("{}", self.borders.len()),
49            };
50            w.start_tag_with_attrs(tag, attrs)?;
51            for border in self.borders.iter() {
52                border.write_xml(w)?;
53            }
54            w.end_tag(tag)?;
55        }
56        Ok(())
57    }
58}
59
60#[derive(Default, Clone, Hash, PartialEq, Eq, Debug)]
61pub struct CellBorder {
62    pub left: Option<Border>,
63    pub right: Option<Border>,
64    pub top: Option<Border>,
65    pub bottom: Option<Border>,
66    pub diagonal: Option<DiagonalBorder>,
67}
68
69impl CellBorder {
70    pub fn set_around(&mut self, border: Option<Border>) {
71        self.left = border;
72        self.right = border;
73        self.top = border;
74        self.bottom = border;
75    }
76}
77
78#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
79pub struct DiagonalBorder {
80    pub diagonal_type: DiagonalBorderType,
81    pub style: BorderStyle,
82    pub color: Option<RGB8>,
83}
84
85#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
86pub struct Border {
87    pub style: BorderStyle,
88    /// If this is None, the color will be set to 'auto'.
89    pub color: Option<RGB8>,
90}
91
92#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
93pub enum DiagonalBorderType {
94    Up,
95    Down,
96    UpDown,
97}
98
99#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
100pub enum BorderStyle {
101    /// Can be used to override a cell border if e.g. a row or column
102    /// border was set.
103    None,
104    Thin,
105    Medium,
106    Dashed,
107    Dotted,
108    Thick,
109    Double,
110    Hair,
111    MediumDashed,
112    DashDot,
113    MediumDashDot,
114    DashDotDot,
115    MediumDashDotDot,
116    SlantDashDot,
117}
118
119impl AsRef<str> for BorderStyle {
120    fn as_ref(&self) -> &str {
121        match self {
122            BorderStyle::None => "none",
123            BorderStyle::Thin => "thin",
124            BorderStyle::Medium => "medium",
125            BorderStyle::Dashed => "dashed",
126            BorderStyle::Dotted => "dotted",
127            BorderStyle::Thick => "thick",
128            BorderStyle::Double => "dobule",
129            BorderStyle::Hair => "hair",
130            BorderStyle::MediumDashed => "mediumDashed",
131            BorderStyle::DashDot => "dashDot",
132            BorderStyle::MediumDashDot => "mediumDashDot",
133            BorderStyle::DashDotDot => "dashDotDot",
134            BorderStyle::MediumDashDotDot => "mediumDashDotDot",
135            BorderStyle::SlantDashDot => "slantDashDot",
136        }
137    }
138}
139
140impl XmlWritable for CellBorder {
141    fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
142        let tag = "border";
143
144        let attrs = if let Some(ref border) = self.diagonal {
145            match border.diagonal_type {
146                DiagonalBorderType::Up => {
147                    indexmap! {"diagonalUp" => "1"}
148                }
149                DiagonalBorderType::Down => {
150                    indexmap! {"diagonalDown" => "1"}
151                }
152                DiagonalBorderType::UpDown => {
153                    indexmap! {
154                        "diagonalUp" => "1",
155                        "diagonalDown" => "1",
156                    }
157                }
158            }
159        } else {
160            indexmap! {}
161        };
162
163        w.start_tag_with_attrs(tag, attrs)?;
164        self.write_left(w)?;
165        self.write_right(w)?;
166        self.write_top(w)?;
167        self.write_bottom(w)?;
168        self.write_diagonal(w)?;
169        w.end_tag(tag)?;
170        Ok(())
171    }
172}
173
174impl CellBorder {
175    fn write_left<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
176        Self::write_optional_border(w, "left", self.left.as_ref())
177    }
178
179    fn write_right<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
180        Self::write_optional_border(w, "right", self.right.as_ref())
181    }
182
183    fn write_top<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
184        Self::write_optional_border(w, "top", self.top.as_ref())
185    }
186
187    fn write_bottom<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
188        Self::write_optional_border(w, "bottom", self.bottom.as_ref())
189    }
190
191    fn write_diagonal<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
192        Self::write_optional_border(
193            w,
194            "diagonal",
195            self.diagonal
196                .map(|d| Border {
197                    style: d.style,
198                    color: d.color,
199                })
200                .as_ref(),
201        )
202    }
203
204    fn write_optional_border<W: XmlWriter>(
205        w: &mut W,
206        name: &str,
207        border: Option<&Border>,
208    ) -> Result<()> {
209        if let Some(b) = border {
210            b.write_xml(w, name)
211        } else {
212            w.empty_tag(name)
213        }
214    }
215}
216
217impl Border {
218    fn write_xml<W: XmlWriter>(
219        &self,
220        w: &mut W,
221        name: &str,
222    ) -> Result<()> {
223        let attrs = indexmap! {
224            "style" => self.style.as_ref()
225        };
226        w.start_tag_with_attrs(name, attrs)?;
227        {
228            let mut attrs = IndexMap::new();
229            if let Some(color) = self.color {
230                attrs.insert("rgb", color.as_palette_color());
231            } else {
232                attrs.insert("auto", "1".to_string());
233            }
234            w.empty_tag_with_attrs("color", attrs)?;
235        }
236        w.end_tag(name)?;
237        Ok(())
238    }
239}