docx_rust/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, Hyperlink, Run, RunContent,
10 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 )]
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#[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);