docx_rust/document/
body.rs

1use derive_more::From;
2use hard_xml::{XmlRead, XmlWrite};
3use std::borrow::Borrow;
4
5use crate::__xml_test_suites;
6use crate::document::{Paragraph, Run, Table, TableCell};
7use crate::formatting::SectionProperty;
8
9use super::SDT;
10
11/// Document Body
12///
13/// This is the main document editing surface.
14#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
15#[cfg_attr(test, derive(PartialEq))]
16#[xml(tag = "w:body")]
17pub struct Body<'a> {
18    /// Specifies the contents of the body of the document.
19    #[xml(child = "w:p", child = "w:tbl", child = "w:sectPr", child = "w:sdt")]
20    pub content: Vec<BodyContent<'a>>,
21}
22
23impl<'a> Body<'a> {
24    pub fn push<T: Into<BodyContent<'a>>>(&mut self, content: T) -> &mut Self {
25        self.content.push(content.into());
26        self
27    }
28
29    pub fn text(&self) -> String {
30        let v: Vec<_> = self
31            .content
32            .iter()
33            .filter_map(|content| match content {
34                BodyContent::Paragraph(para) => Some(para.text()),
35                BodyContent::Table(_) => None,
36                BodyContent::SectionProperty(_) => None,
37                BodyContent::Sdt(sdt) => Some(sdt.text()),
38                BodyContent::TableCell(_) => None,
39                BodyContent::Run(_) => None,
40            })
41            .collect();
42        v.join("\r\n")
43    }
44
45    pub fn replace_text_simple<S>(&mut self, old: S, new: S)
46    where
47        S: AsRef<str>,
48    {
49        let _d = self.replace_text(&[(old, new)]);
50    }
51
52    pub fn replace_text<'b, I, T, S>(&mut self, dic: T) -> crate::DocxResult<()>
53    where
54        S: AsRef<str> + 'b,
55        T: IntoIterator<Item = I> + Copy,
56        I: Borrow<(S, S)>,
57    {
58        for content in self.content.iter_mut() {
59            match content {
60                BodyContent::Paragraph(p) => {
61                    p.replace_text(dic)?;
62                }
63                BodyContent::Table(t) => {
64                    t.replace_text(dic)?;
65                }
66                BodyContent::SectionProperty(_) => {}
67                BodyContent::Sdt(_) => {}
68                BodyContent::TableCell(_) => {}
69                BodyContent::Run(_) => {}
70            }
71        }
72        Ok(())
73    }
74
75    // pub fn iter_text(&self) -> impl Iterator<Item = &Cow<'a, str>> {
76    //     self.content
77    //         .iter()
78    //         .filter_map(|content| match content {
79    //             BodyContent::Paragraph(para) => Some(para.iter_text()),
80    //         })
81    //         .flatten()
82    // }
83
84    // pub fn iter_text_mut(&mut self) -> impl Iterator<Item = &mut Cow<'a, str>> {
85    //     self.content
86    //         .iter_mut()
87    //         .filter_map(|content| match content {
88    //             BodyContent::Paragraph(para) => Some(para.iter_text_mut()),
89    //         })
90    //         .flatten()
91    // }
92}
93
94/// A set of elements that can be contained in the body
95#[derive(Debug, From, XmlRead, XmlWrite, Clone)]
96#[cfg_attr(test, derive(PartialEq))]
97pub enum BodyContent<'a> {
98    #[xml(tag = "w:p")]
99    Paragraph(Paragraph<'a>),
100    #[xml(tag = "w:tbl")]
101    Table(Table<'a>),
102    #[xml(tag = "w:sdt")]
103    Sdt(SDT<'a>),
104    #[xml(tag = "w:sectPr")]
105    SectionProperty(SectionProperty<'a>),
106    #[xml(tag = "w:tc")]
107    TableCell(TableCell<'a>),
108    #[xml(tag = "w:r")]
109    Run(Run<'a>),
110}
111
112__xml_test_suites!(
113    Body,
114    Body::default(),
115    r#"<w:body/>"#,
116    Body {
117        content: vec![Paragraph::default().into()]
118    },
119    r#"<w:body><w:p/></w:body>"#,
120    Body {
121        content: vec![Table::default().into()]
122    },
123    r#"<w:body><w:tbl><w:tblPr/><w:tblGrid/></w:tbl></w:body>"#,
124);