docx_rs/documents/elements/
table_borders.rs

1use serde::Serialize;
2use std::io::Write;
3
4use crate::documents::BuildXML;
5use crate::types::*;
6use crate::xml_builder::*;
7
8/*
9    Please see. L.4.3.2.18 Cell Border Properties
10
11    left – left border
12    right – right border
13    top – top border
14    bottom – bottom border
15    insideH – inner horizontal borders
16    insideV – inner vertical borders
17    tl2br – diagonal border from top left corner to bottom right corner
18    tr2bl – diagonal border from top right corner to bottom left corner
19*/
20#[derive(Serialize, Debug, Clone, PartialEq)]
21#[serde(rename_all = "camelCase")]
22pub struct TableBorder {
23    pub border_type: BorderType,
24    pub size: usize,
25    pub color: String,
26    position: TableBorderPosition,
27    space: usize,
28}
29
30impl TableBorder {
31    pub fn new(position: TableBorderPosition) -> TableBorder {
32        TableBorder {
33            position,
34            border_type: BorderType::Single,
35            size: 2,
36            space: 0,
37            color: "000000".to_owned(),
38        }
39    }
40
41    pub fn color(mut self, color: impl Into<String>) -> TableBorder {
42        self.color = color.into();
43        self
44    }
45
46    pub fn size(mut self, size: usize) -> TableBorder {
47        self.size = size;
48        self
49    }
50
51    pub fn border_type(mut self, border_type: BorderType) -> TableBorder {
52        self.border_type = border_type;
53        self
54    }
55}
56
57impl BuildXML for TableBorder {
58    fn build_to<W: Write>(
59        &self,
60        stream: xml::writer::EventWriter<W>,
61    ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
62        let func = match self.position {
63            TableBorderPosition::Top => XMLBuilder::border_top,
64            TableBorderPosition::Left => XMLBuilder::border_left,
65            TableBorderPosition::Bottom => XMLBuilder::border_bottom,
66            TableBorderPosition::Right => XMLBuilder::border_right,
67            TableBorderPosition::InsideH => XMLBuilder::border_inside_h,
68            TableBorderPosition::InsideV => XMLBuilder::border_inside_v,
69        };
70
71        XMLBuilder::from(stream)
72            .apply(|b| func(b, self.border_type, self.size, self.space, &self.color))?
73            .into_inner()
74    }
75}
76
77#[derive(Serialize, Debug, Clone, PartialEq)]
78#[serde(rename_all = "camelCase")]
79pub struct TableBorders {
80    top: Option<TableBorder>,
81    left: Option<TableBorder>,
82    bottom: Option<TableBorder>,
83    right: Option<TableBorder>,
84    inside_h: Option<TableBorder>,
85    inside_v: Option<TableBorder>,
86}
87
88impl Default for TableBorders {
89    fn default() -> TableBorders {
90        TableBorders {
91            top: Some(TableBorder::new(TableBorderPosition::Top)),
92            left: Some(TableBorder::new(TableBorderPosition::Left)),
93            bottom: Some(TableBorder::new(TableBorderPosition::Bottom)),
94            right: Some(TableBorder::new(TableBorderPosition::Right)),
95            inside_h: Some(TableBorder::new(TableBorderPosition::InsideH)),
96            inside_v: Some(TableBorder::new(TableBorderPosition::InsideV)),
97        }
98    }
99}
100
101impl TableBorders {
102    pub fn new() -> TableBorders {
103        Default::default()
104    }
105
106    pub fn with_empty() -> TableBorders {
107        TableBorders {
108            top: None,
109            left: None,
110            bottom: None,
111            right: None,
112            inside_h: None,
113            inside_v: None,
114        }
115    }
116
117    pub fn set(mut self, border: TableBorder) -> Self {
118        match border.position {
119            TableBorderPosition::Top => self.top = Some(border),
120            TableBorderPosition::Left => self.left = Some(border),
121            TableBorderPosition::Bottom => self.bottom = Some(border),
122            TableBorderPosition::Right => self.right = Some(border),
123            TableBorderPosition::InsideH => self.inside_h = Some(border),
124            TableBorderPosition::InsideV => self.inside_v = Some(border),
125        };
126        self
127    }
128
129    pub fn clear(mut self, position: TableBorderPosition) -> Self {
130        let nil = TableBorder::new(position.clone()).border_type(BorderType::Nil);
131        match position {
132            TableBorderPosition::Top => self.top = Some(nil),
133            TableBorderPosition::Left => self.left = Some(nil),
134            TableBorderPosition::Bottom => self.bottom = Some(nil),
135            TableBorderPosition::Right => self.right = Some(nil),
136            TableBorderPosition::InsideH => self.inside_h = Some(nil),
137            TableBorderPosition::InsideV => self.inside_v = Some(nil),
138        };
139        self
140    }
141
142    pub fn clear_all(mut self) -> Self {
143        self.top = Some(TableBorder::new(TableBorderPosition::Top).border_type(BorderType::Nil));
144        self.left = Some(TableBorder::new(TableBorderPosition::Left).border_type(BorderType::Nil));
145        self.bottom =
146            Some(TableBorder::new(TableBorderPosition::Bottom).border_type(BorderType::Nil));
147        self.right =
148            Some(TableBorder::new(TableBorderPosition::Right).border_type(BorderType::Nil));
149        self.inside_h =
150            Some(TableBorder::new(TableBorderPosition::InsideH).border_type(BorderType::Nil));
151        self.inside_v =
152            Some(TableBorder::new(TableBorderPosition::InsideV).border_type(BorderType::Nil));
153        self
154    }
155}
156
157impl BuildXML for TableBorders {
158    fn build_to<W: Write>(
159        &self,
160        stream: xml::writer::EventWriter<W>,
161    ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
162        XMLBuilder::from(stream)
163            .open_table_borders()?
164            .add_optional_child(&self.top)?
165            .add_optional_child(&self.left)?
166            .add_optional_child(&self.bottom)?
167            .add_optional_child(&self.right)?
168            .add_optional_child(&self.inside_h)?
169            .add_optional_child(&self.inside_v)?
170            .close()?
171            .into_inner()
172    }
173}
174
175#[cfg(test)]
176mod tests {
177
178    use super::*;
179    #[cfg(test)]
180    use pretty_assertions::assert_eq;
181    use std::str;
182
183    #[test]
184    fn test_table_borders() {
185        let b = TableBorders::new().build();
186        assert_eq!(
187            str::from_utf8(&b).unwrap(),
188            r#"<w:tblBorders><w:top w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:left w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:bottom w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:right w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:insideH w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:insideV w:val="single" w:sz="2" w:space="0" w:color="000000" /></w:tblBorders>"#
189        );
190    }
191
192    #[test]
193    fn test_table_borders_set() {
194        let b = TableBorders::new()
195            .set(TableBorder::new(TableBorderPosition::Left).color("AAAAAA"))
196            .clear(TableBorderPosition::Top)
197            .build();
198        assert_eq!(
199            str::from_utf8(&b).unwrap(),
200            r#"<w:tblBorders><w:top w:val="nil" w:sz="2" w:space="0" w:color="000000" /><w:left w:val="single" w:sz="2" w:space="0" w:color="AAAAAA" /><w:bottom w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:right w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:insideH w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:insideV w:val="single" w:sz="2" w:space="0" w:color="000000" /></w:tblBorders>"#
201        );
202    }
203}