Skip to main content

rs_docx/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(
20        child = "w:p",
21        child = "w:tbl",
22        child = "w:sectPr",
23        child = "w:sdt",
24        child = "w:bookmarkStart",
25        child = "w:bookmarkEnd"
26    )]
27    pub content: Vec<BodyContent<'a>>,
28}
29
30// ... (existing impl block)
31
32/// A set of elements that can be contained in the body
33#[derive(Debug, From, XmlRead, XmlWrite, Clone)]
34#[cfg_attr(test, derive(PartialEq))]
35#[allow(clippy::large_enum_variant)]
36pub enum BodyContent<'a> {
37    #[xml(tag = "w:p")]
38    Paragraph(Paragraph<'a>),
39    #[xml(tag = "w:tbl")]
40    Table(Table<'a>),
41    #[xml(tag = "w:sdt")]
42    Sdt(SDT<'a>),
43    #[xml(tag = "w:sectPr")]
44    SectionProperty(SectionProperty<'a>),
45    #[xml(tag = "w:tc")]
46    TableCell(TableCell<'a>),
47    #[xml(tag = "w:r")]
48    Run(Run<'a>),
49    #[xml(tag = "w:bookmarkStart")]
50    BookmarkStart(crate::document::BookmarkStart<'a>),
51    #[xml(tag = "w:bookmarkEnd")]
52    BookmarkEnd(crate::document::BookmarkEnd<'a>),
53}
54
55impl<'a> Body<'a> {
56    pub fn push<T: Into<BodyContent<'a>>>(&mut self, content: T) -> &mut Self {
57        self.content.push(content.into());
58        self
59    }
60
61    pub fn text(&self) -> String {
62        let v: Vec<_> = self
63            .content
64            .iter()
65            .filter_map(|content| match content {
66                BodyContent::Paragraph(para) => Some(para.text()),
67                BodyContent::Table(_) => None,
68                BodyContent::SectionProperty(_) => None,
69                BodyContent::Sdt(sdt) => Some(sdt.text()),
70                BodyContent::TableCell(_) => None,
71                BodyContent::Run(_) => None,
72                BodyContent::BookmarkStart(_) => None,
73                BodyContent::BookmarkEnd(_) => None,
74            })
75            .collect();
76        v.join("\r\n")
77    }
78
79    pub fn replace_text_simple<S>(&mut self, old: S, new: S)
80    where
81        S: AsRef<str>,
82    {
83        let _d = self.replace_text(&[(old, new)]);
84    }
85
86    pub fn replace_text<'b, I, T, S>(&mut self, dic: T) -> crate::DocxResult<()>
87    where
88        S: AsRef<str> + 'b,
89        T: IntoIterator<Item = I> + Copy,
90        I: Borrow<(S, S)>,
91    {
92        for content in self.content.iter_mut() {
93            match content {
94                BodyContent::Paragraph(p) => {
95                    p.replace_text(dic)?;
96                }
97                BodyContent::Table(t) => {
98                    t.replace_text(dic)?;
99                }
100                BodyContent::SectionProperty(_) => {}
101                BodyContent::Sdt(_) => {}
102                BodyContent::TableCell(_) => {}
103                BodyContent::Run(_) => {}
104                BodyContent::BookmarkStart(_) => {}
105                BodyContent::BookmarkEnd(_) => {}
106            }
107        }
108        Ok(())
109    }
110
111    // pub fn iter_text(&self) -> impl Iterator<Item = &Cow<'a, str>> {
112    //     self.content
113    //         .iter()
114    //         .filter_map(|content| match content {
115    //             BodyContent::Paragraph(para) => Some(para.iter_text()),
116    //         })
117    //         .flatten()
118    // }
119
120    // pub fn iter_text_mut(&mut self) -> impl Iterator<Item = &mut Cow<'a, str>> {
121    //     self.content
122    //         .iter_mut()
123    //         .filter_map(|content| match content {
124    //             BodyContent::Paragraph(para) => Some(para.iter_text_mut()),
125    //         })
126    //         .flatten()
127    // }
128}
129
130__xml_test_suites!(
131    Body,
132    Body::default(),
133    r#"<w:body/>"#,
134    Body {
135        content: vec![Paragraph::default().into()]
136    },
137    r#"<w:body><w:p/></w:body>"#,
138    Body {
139        content: vec![Table::default().into()]
140    },
141    r#"<w:body><w:tbl><w:tblPr/><w:tblGrid/></w:tbl></w:body>"#,
142);