rs_docx/document/
paragraph.rs1#![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, Deletion, Hyperlink,
10 Insertion, Run, RunContent, Text, SDT,
11 },
12 formatting::ParagraphProperty,
13};
14
15#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
32#[cfg_attr(test, derive(PartialEq))]
33#[xml(tag = "w:p")]
34pub struct Paragraph<'a> {
35 #[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 #[xml(child = "w:pPr")]
47 pub property: Option<ParagraphProperty<'a>>,
48 #[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 child = "w:ins",
60 child = "w:del"
61 )]
62 pub content: Vec<ParagraphContent<'a>>,
63}
64
65impl<'a> Paragraph<'a> {
66 __setter!(property: Option<ParagraphProperty<'a>>);
67
68 #[inline(always)]
69 pub fn push<T: Into<ParagraphContent<'a>>>(mut self, content: T) -> Self {
70 self.content.push(content.into());
71 self
72 }
73
74 #[inline(always)]
75 pub fn push_text<T: Into<Text<'a>>>(mut self, content: T) -> Self {
76 self.content.push(ParagraphContent::Run(Run {
77 content: vec![RunContent::Text(content.into())],
78 ..Default::default()
79 }));
80 self
81 }
82
83 pub fn text(&self) -> String {
84 self.iter_text()
85 .map(|c| c.to_string())
86 .collect::<Vec<_>>()
87 .join("")
88 }
89
90 pub fn iter_text(&self) -> Box<dyn Iterator<Item = &Cow<'a, str>> + '_> {
91 Box::new(
92 self.content
93 .iter()
94 .filter_map(|content| match content {
95 ParagraphContent::Run(run) => Some(run.iter_text()),
96 ParagraphContent::Link(link) => Some(link.iter_text()),
97 ParagraphContent::SDT(sdt) => Some(sdt.iter_text()),
98 _ => None,
99 })
100 .flatten(),
101 )
102 }
103
104 pub fn iter_text_mut(&mut self) -> impl Iterator<Item = &mut Cow<'a, str>> {
105 self.content
106 .iter_mut()
107 .filter_map(|content| match content {
108 ParagraphContent::Run(run) => Some(run.iter_text_mut()),
109 ParagraphContent::Link(link) => Some(link.iter_text_mut()),
110 _ => None,
111 })
112 .flatten()
113 }
114
115 pub fn replace_text<'b, I, T, S>(&mut self, dic: T) -> crate::DocxResult<()>
116 where
117 S: AsRef<str> + 'b,
118 T: IntoIterator<Item = I> + Copy,
119 I: Borrow<(S, S)>,
120 {
121 for content in self.content.iter_mut() {
122 if let ParagraphContent::Run(r) = content {
123 r.replace_text(dic)?;
124 }
125 }
126
127 Ok(())
128 }
129}
130
131#[derive(Debug, From, XmlRead, XmlWrite, Clone)]
133#[cfg_attr(test, derive(PartialEq))]
134#[allow(clippy::large_enum_variant)]
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 #[xml(tag = "w:ins")]
151 Insertion(Insertion<'a>),
152 #[xml(tag = "w:del")]
153 Deletion(Deletion<'a>),
154}
155
156__xml_test_suites!(
157 Paragraph,
158 Paragraph::default(),
159 r#"<w:p/>"#,
160 Paragraph::default().push(Run::default()),
161 r#"<w:p><w:r/></w:p>"#,
162 Paragraph::default().push(Hyperlink::default()),
163 r#"<w:p><w:hyperlink/></w:p>"#,
164 Paragraph::default().push(BookmarkStart::default()),
165 r#"<w:p><w:bookmarkStart/></w:p>"#,
166 Paragraph::default().push(BookmarkEnd::default()),
167 r#"<w:p><w:bookmarkEnd/></w:p>"#,
168);