docx_rs/documents/elements/
table_cell.rs

1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::io::Write;
4
5use super::*;
6use crate::documents::BuildXML;
7use crate::types::*;
8use crate::xml_builder::*;
9
10#[derive(Serialize, Debug, Clone, PartialEq)]
11#[serde(rename_all = "camelCase")]
12pub struct TableCell {
13    pub children: Vec<TableCellContent>,
14    pub property: TableCellProperty,
15    pub has_numbering: bool,
16}
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum TableCellContent {
20    Paragraph(Paragraph),
21    Table(Table),
22    StructuredDataTag(Box<StructuredDataTag>),
23    TableOfContents(Box<TableOfContents>),
24}
25
26impl Serialize for TableCellContent {
27    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
28    where
29        S: Serializer,
30    {
31        match *self {
32            TableCellContent::Paragraph(ref s) => {
33                let mut t = serializer.serialize_struct("Paragraph", 2)?;
34                t.serialize_field("type", "paragraph")?;
35                t.serialize_field("data", s)?;
36                t.end()
37            }
38            TableCellContent::Table(ref s) => {
39                let mut t = serializer.serialize_struct("Table", 2)?;
40                t.serialize_field("type", "table")?;
41                t.serialize_field("data", s)?;
42                t.end()
43            }
44            TableCellContent::StructuredDataTag(ref r) => {
45                let mut t = serializer.serialize_struct("StructuredDataTag", 2)?;
46                t.serialize_field("type", "structuredDataTag")?;
47                t.serialize_field("data", r)?;
48                t.end()
49            }
50            TableCellContent::TableOfContents(ref r) => {
51                let mut t = serializer.serialize_struct("TableOfContents", 2)?;
52                t.serialize_field("type", "tableOfContents")?;
53                t.serialize_field("data", r)?;
54                t.end()
55            }
56        }
57    }
58}
59
60impl TableCell {
61    pub fn new() -> TableCell {
62        Default::default()
63    }
64
65    pub fn add_paragraph(mut self, p: Paragraph) -> TableCell {
66        if p.has_numbering {
67            self.has_numbering = true
68        }
69        self.children.push(TableCellContent::Paragraph(p));
70        self
71    }
72
73    pub fn add_table_of_contents(mut self, t: TableOfContents) -> Self {
74        self.children
75            .push(TableCellContent::TableOfContents(Box::new(t)));
76        self
77    }
78
79    pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self {
80        self.children
81            .push(TableCellContent::StructuredDataTag(Box::new(t)));
82        self
83    }
84
85    pub fn add_table(mut self, t: Table) -> TableCell {
86        if t.has_numbering {
87            self.has_numbering = true
88        }
89        self.children.push(TableCellContent::Table(t));
90        self
91    }
92
93    pub fn vertical_merge(mut self, t: VMergeType) -> TableCell {
94        self.property = self.property.vertical_merge(t);
95        self
96    }
97
98    pub fn shading(mut self, s: Shading) -> TableCell {
99        self.property = self.property.shading(s);
100        self
101    }
102
103    pub fn vertical_align(mut self, t: VAlignType) -> TableCell {
104        self.property = self.property.vertical_align(t);
105        self
106    }
107
108    pub fn text_direction(mut self, t: TextDirectionType) -> TableCell {
109        self.property = self.property.text_direction(t);
110        self
111    }
112
113    pub fn grid_span(mut self, v: usize) -> TableCell {
114        self.property = self.property.grid_span(v);
115        self
116    }
117
118    pub fn width(mut self, v: usize, t: WidthType) -> TableCell {
119        self.property = self.property.width(v, t);
120        self
121    }
122
123    pub fn set_border(mut self, border: TableCellBorder) -> Self {
124        self.property = self.property.set_border(border);
125        self
126    }
127
128    pub fn set_borders(mut self, borders: TableCellBorders) -> Self {
129        self.property = self.property.set_borders(borders);
130        self
131    }
132
133    pub fn clear_border(mut self, position: TableCellBorderPosition) -> Self {
134        self.property = self.property.clear_border(position);
135        self
136    }
137
138    pub fn clear_all_border(mut self) -> Self {
139        self.property = self.property.clear_all_border();
140        self
141    }
142}
143
144impl Default for TableCell {
145    fn default() -> Self {
146        let property = TableCellProperty::new();
147        let children = vec![];
148        Self {
149            property,
150            children,
151            has_numbering: false,
152        }
153    }
154}
155
156impl BuildXML for TableCell {
157    fn build_to<W: Write>(
158        &self,
159        stream: xml::writer::EventWriter<W>,
160    ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
161        XMLBuilder::from(stream)
162            .open_table_cell()?
163            .add_child(&self.property)?
164            .apply_each(&self.children, |ch, b| {
165                match ch {
166                    TableCellContent::Paragraph(p) => b.add_child(p),
167                    TableCellContent::Table(t) => {
168                        b.add_child(t)?
169                            // INFO: We need to add empty paragraph when parent cell includes only cell.
170                            .apply_if(self.children.len() == 1, |b| b.add_child(&Paragraph::new()))
171                    }
172                    TableCellContent::StructuredDataTag(t) => b.add_child(&t),
173                    TableCellContent::TableOfContents(t) => b.add_child(&t),
174                }
175            })?
176            // INFO: We need to add empty paragraph when parent cell includes only cell.
177            .apply_if(self.children.is_empty(), |b| b.add_child(&Paragraph::new()))?
178            .close()?
179            .into_inner()
180    }
181}
182
183#[cfg(test)]
184mod tests {
185
186    use super::*;
187    #[cfg(test)]
188    use pretty_assertions::assert_eq;
189    use std::str;
190
191    #[test]
192    fn test_cell() {
193        let b = TableCell::new().build();
194        assert_eq!(
195            str::from_utf8(&b).unwrap(),
196            r#"<w:tc><w:tcPr /><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr></w:p></w:tc>"#
197        );
198    }
199
200    #[test]
201    fn test_cell_add_p() {
202        let b = TableCell::new()
203            .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")))
204            .build();
205        assert_eq!(
206            str::from_utf8(&b).unwrap(),
207            r#"<w:tc><w:tcPr /><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p></w:tc>"#
208        );
209    }
210
211    #[test]
212    fn test_cell_json() {
213        let c = TableCell::new()
214            .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")))
215            .grid_span(2);
216        assert_eq!(
217            serde_json::to_string(&c).unwrap(),
218            r#"{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}}],"property":{"width":null,"borders":null,"gridSpan":2,"verticalMerge":null,"verticalAlign":null,"textDirection":null,"shading":null},"hasNumbering":false}"#,
219        );
220    }
221}