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 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 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}