docx_rust/document/
paragraph.rs

1#![allow(unused_must_use)]
2use derive_more::From;
3use hard_xml::{XmlRead, XmlWrite};
4use std::borrow::{Borrow, Cow};
5
6use crate::{
7    __setter, __xml_test_suites,
8    document::{
9        BookmarkEnd, BookmarkStart, CommentRangeEnd, CommentRangeStart, Hyperlink, Run, RunContent,
10        Text, SDT,
11    },
12    formatting::ParagraphProperty,
13};
14
15/// Paragraph
16///
17/// Paragraph is the main block-level container for content.
18///
19/// ```rust
20/// use docx_rust::document::*;
21/// use docx_rust::formatting::*;
22///
23/// let par = Paragraph::default()
24///     .property(ParagraphProperty::default())
25///     .push_text("hello,")
26///     .push_text((" world.", TextSpace::Preserve))
27///     .push(Run::default())
28///     .push(BookmarkStart::default())
29///     .push(BookmarkEnd::default());
30/// ```
31#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
32#[cfg_attr(test, derive(PartialEq))]
33#[xml(tag = "w:p")]
34pub struct Paragraph<'a> {
35    //#[xml(attr = "w14:paraId")]
36    //pub id: Option<Cow<'a, str>>,
37    //#[xml(attr = "w14:textId")]
38    //pub text_id: Option<Cow<'a, str>>,
39    #[xml(attr = "w:rsidR")]
40    pub rsid_r: Option<Cow<'a, str>>,
41    #[xml(attr = "w:rsidRDefault")]
42    pub rsid_r_default: Option<Cow<'a, str>>,
43    /// Specifies the properties of a paragraph
44    ///
45    /// This information is applied to all the contents of the paragraph.
46    #[xml(child = "w:pPr")]
47    pub property: Option<ParagraphProperty<'a>>,
48    /// Specifes the run contents of a paragraph
49    ///
50    /// Run is a region of text with properties. Each paragraph containes one or more runs.
51    #[xml(
52        child = "w:commentRangeStart",
53        child = "w:commentRangeEnd",
54        child = "w:r",
55        child = "w:hyperlink",
56        child = "w:bookmarkStart",
57        child = "w:bookmarkEnd",
58        child = "w:sdt"
59    )]
60    pub content: Vec<ParagraphContent<'a>>,
61}
62
63impl<'a> Paragraph<'a> {
64    __setter!(property: Option<ParagraphProperty<'a>>);
65
66    #[inline(always)]
67    pub fn push<T: Into<ParagraphContent<'a>>>(mut self, content: T) -> Self {
68        self.content.push(content.into());
69        self
70    }
71
72    #[inline(always)]
73    pub fn push_text<T: Into<Text<'a>>>(mut self, content: T) -> Self {
74        self.content.push(ParagraphContent::Run(Run {
75            content: vec![RunContent::Text(content.into())],
76            ..Default::default()
77        }));
78        self
79    }
80
81    pub fn text(&self) -> String {
82        self.iter_text()
83            .map(|c| c.to_string())
84            .collect::<Vec<_>>()
85            .join("")
86    }
87
88    pub fn iter_text(&self) -> Box<dyn Iterator<Item = &Cow<'a, str>> + '_> {
89        Box::new(
90            self.content
91                .iter()
92                .filter_map(|content| match content {
93                    ParagraphContent::Run(run) => Some(run.iter_text()),
94                    ParagraphContent::Link(link) => Some(link.iter_text()),
95                    ParagraphContent::SDT(sdt) => Some(sdt.iter_text()),
96                    _ => None,
97                })
98                .flatten(),
99        )
100    }
101
102    pub fn iter_text_mut(&mut self) -> impl Iterator<Item = &mut Cow<'a, str>> {
103        self.content
104            .iter_mut()
105            .filter_map(|content| match content {
106                ParagraphContent::Run(run) => Some(run.iter_text_mut()),
107                ParagraphContent::Link(link) => Some(link.iter_text_mut()),
108                _ => None,
109            })
110            .flatten()
111    }
112
113    pub fn replace_text<'b, I, T, S>(&mut self, dic: T) -> crate::DocxResult<()>
114    where
115        S: AsRef<str> + 'b,
116        T: IntoIterator<Item = I> + Copy,
117        I: Borrow<(S, S)>,
118    {
119        for content in self.content.iter_mut() {
120            match content {
121                ParagraphContent::Run(r) => {
122                    r.replace_text(dic)?;
123                }
124                _ => {}
125            }
126        }
127
128        Ok(())
129    }
130}
131
132/// A set of elements that can be contained as the content of a paragraph.
133#[derive(Debug, From, XmlRead, XmlWrite, Clone)]
134#[cfg_attr(test, derive(PartialEq))]
135pub enum ParagraphContent<'a> {
136    #[xml(tag = "w:commentRangeStart")]
137    CommentRangeStart(CommentRangeStart<'a>),
138    #[xml(tag = "w:commentRangeEnd")]
139    CommentRangeEnd(CommentRangeEnd<'a>),
140    #[xml(tag = "w:r")]
141    Run(Run<'a>),
142    #[xml(tag = "w:hyperlink")]
143    Link(Hyperlink<'a>),
144    #[xml(tag = "w:bookmarkStart")]
145    BookmarkStart(BookmarkStart<'a>),
146    #[xml(tag = "w:bookmarkEnd")]
147    BookmarkEnd(BookmarkEnd<'a>),
148    #[xml(tag = "w:sdt")]
149    SDT(SDT<'a>),
150}
151
152__xml_test_suites!(
153    Paragraph,
154    Paragraph::default(),
155    r#"<w:p/>"#,
156    Paragraph::default().push(Run::default()),
157    r#"<w:p><w:r/></w:p>"#,
158    Paragraph::default().push(Hyperlink::default()),
159    r#"<w:p><w:hyperlink/></w:p>"#,
160    Paragraph::default().push(BookmarkStart::default()),
161    r#"<w:p><w:bookmarkStart/></w:p>"#,
162    Paragraph::default().push(BookmarkEnd::default()),
163    r#"<w:p><w:bookmarkEnd/></w:p>"#,
164);