Skip to main content

rs_docx/document/
run.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        drawing::Drawing, field_char::FieldChar, instrtext::InstrText, pict::Pict, r#break::Break,
10        r#break::LastRenderedPageBreak, tab::Tab, text::Text,
11    },
12    formatting::CharacterProperty,
13    DocxResult, __define_enum, __define_struct,
14};
15
16use super::{
17    date::{DayLong, DayShort, MonthLong, MonthShort, YearLong, YearShort},
18    instrtext::DelInstrText,
19    sym::Sym,
20    AnnotationRef, CarriageReturn, CommentReference, DelText, EndnoteRef, EndnoteReference,
21    FootnoteRef, FootnoteReference,
22};
23
24/// Run
25///
26/// Run is a non-block region of text with properties.
27///
28/// ```rust
29/// use rs_docx::document::*;
30/// use rs_docx::formatting::*;
31///
32/// let run = Run::default()
33///     .property(CharacterProperty::default())
34///     .push_text("text")
35///     .push_break(None)
36///     .push_text((" text ", TextSpace::Preserve))
37///     .push_break(BreakType::Column);
38/// ```
39#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
40#[cfg_attr(test, derive(PartialEq))]
41#[xml(tag = "w:r")]
42pub struct Run<'a> {
43    /// Specifies the properties of a run
44    ///
45    #[xml(attr = "w:rsidR")]
46    pub rsid_r: Option<Cow<'a, str>>,
47    #[xml(attr = "w:rsidRDefault")]
48    pub rsid_r_default: Option<Cow<'a, str>>,
49    /// Just as paragraph, a run's properties is applied to all the contents of the run.
50    #[xml(child = "w:rPr")]
51    pub property: Option<CharacterProperty<'a>>,
52    #[xml(
53        child = "w:br", //Break
54        child = "w:t", //Text
55        child = "w:delText", //Deleted Text
56        child = "w:instrText", //Field Code
57        child = "w:delInstrText", //Deleted Field Code
58        child = "w:noBreakHyphen", //Non Breaking Hyphen Character
59        child = "w:softHyphen", //Optional Hyphen Character
60        child = "w:dayShort", //Date Block - Short Day Format
61        child = "w:monthShort", //Date Block - Short Month Format
62        child = "w:yearShort", //Date Block - Short Year Format
63        child = "w:dayLong", //Date Block - Long Day Format
64        child = "w:monthLong", //Date Block - Long Month Format
65        child = "w:yearLong", //Date Block - Long Year Format
66        child = "w:annotationRef", //Comment Information Block
67        child = "w:footnoteRef", //Footnote Reference Mark
68        child = "w:endnoteRef", //Endnote Reference Mark
69        child = "w:separator", //Footnote/Endnote Separator Mark
70        child = "w:continuationSeparator", //Continuation Separator Mark
71        child = "w:sym", //Symbol Character
72        child = "w:pgNum", //Page Number Block
73        child = "w:cr", //Carriage Return
74        child = "w:tab", //Tab Character
75        //child = "w:object", //Inline Embedded Object
76        child = "w:pict", //VML Object
77        child = "w:fldChar", //Complex Field Character
78        //child = "w:ruby", //Phonetic Guide
79        child = "w:footnoteReference", //Footnote Reference
80        child = "w:endnoteReference", //Endnote Reference
81        child = "w:commentReference", //Comment Content Reference Mark
82        child = "w:drawing", //DrawingML Object
83        child = "w:ptab", //Absolute Position Tab Character
84        child = "w:lastRenderedPageBreak", //Position of Last Calculated Page Break
85    )]
86    /// Specifies the content of a run
87    pub content: Vec<RunContent<'a>>,
88}
89
90impl<'a> Run<'a> {
91    __setter!(property: Option<CharacterProperty<'a>>);
92
93    #[inline(always)]
94    pub fn push<T: Into<RunContent<'a>>>(mut self, content: T) -> Self {
95        self.content.push(content.into());
96        self
97    }
98
99    #[inline(always)]
100    pub fn push_text<T: Into<Text<'a>>>(mut self, content: T) -> Self {
101        self.content.push(RunContent::Text(content.into()));
102        self
103    }
104
105    #[inline(always)]
106    pub fn push_break<T: Into<Break>>(mut self, br: T) -> Self {
107        self.content.push(RunContent::Break(br.into()));
108        self
109    }
110
111    pub fn text(&self) -> String {
112        self.iter_text()
113            .map(|c| c.to_string())
114            .collect::<Vec<_>>()
115            .join("")
116    }
117
118    pub fn iter_text(&self) -> Box<dyn Iterator<Item = &Cow<'a, str>> + '_> {
119        Box::new(self.content.iter().filter_map(|content| match content {
120            RunContent::Text(Text { text, .. }) => Some(text),
121            RunContent::InstrText(InstrText { text, .. }) => Some(text),
122            RunContent::Break(_) => None,
123            RunContent::LastRenderedPageBreak(_) => None,
124            RunContent::FieldChar(_) => None,
125            RunContent::Separator(_) => None,
126            RunContent::ContinuationSeparator(_) => None,
127            RunContent::Tab(_) => None,
128            RunContent::CarriageReturn(_) => None,
129            RunContent::Drawing(_) => None,
130            _ => None,
131        }))
132    }
133
134    pub fn iter_text_mut(&mut self) -> Box<dyn Iterator<Item = &mut Cow<'a, str>> + '_> {
135        Box::new(self.content.iter_mut().filter_map(|content| match content {
136            RunContent::Text(Text { text, .. }) => Some(text),
137            RunContent::InstrText(InstrText { text, .. }) => Some(text),
138            RunContent::Break(_) => None,
139            RunContent::LastRenderedPageBreak(_) => None,
140            RunContent::FieldChar(_) => None,
141            RunContent::Separator(_) => None,
142            RunContent::ContinuationSeparator(_) => None,
143            RunContent::Tab(_) => None,
144            RunContent::CarriageReturn(_) => None,
145            RunContent::Drawing(_) => None,
146            _ => None,
147        }))
148    }
149
150    pub fn replace_text_simple<S>(&mut self, old: S, new: S)
151    where
152        S: AsRef<str>,
153    {
154        self.replace_text(&[(old, new)]);
155    }
156
157    pub fn replace_text<'b, I, T, S>(&mut self, dic: T) -> DocxResult<()>
158    where
159        S: AsRef<str> + 'b,
160        T: IntoIterator<Item = I> + Copy,
161        I: Borrow<(S, S)>,
162    {
163        for c in self.content.iter_mut() {
164            if let RunContent::Text(t) = c {
165                let mut tc = t.text.to_string();
166                for p in dic {
167                    tc = tc.replace(p.borrow().0.as_ref(), p.borrow().1.as_ref());
168                }
169                t.text = tc.into();
170            }
171        }
172
173        Ok(())
174    }
175}
176
177/// A set of elements that can be contained as the content of a run.
178#[derive(Debug, From, XmlRead, XmlWrite, Clone)]
179#[cfg_attr(test, derive(PartialEq))]
180#[allow(clippy::large_enum_variant)]
181pub enum RunContent<'a> {
182    #[xml(tag = "w:br")]
183    Break(Break),
184    #[xml(tag = "w:t")]
185    Text(Text<'a>),
186    #[xml(tag = "w:delText")]
187    DelText(DelText<'a>),
188    #[xml(tag = "w:instrText")]
189    InstrText(InstrText<'a>),
190    #[xml(tag = "w:delInstrText")]
191    DelInstrText(DelInstrText<'a>),
192    #[xml(tag = "w:noBreakHyphen")]
193    NoBreakHyphen(NoBreakHyphen),
194    #[xml(tag = "w:softHyphen")]
195    SoftHyphen(SoftHyphen),
196    #[xml(tag = "w:dayShort")]
197    DayShort(DayShort),
198    #[xml(tag = "w:monthShort")]
199    MonthShort(MonthShort),
200    #[xml(tag = "w:yearShort")]
201    YearShort(YearShort),
202    #[xml(tag = "w:dayLong")]
203    DayLong(DayLong),
204    #[xml(tag = "w:monthLong")]
205    MonthLong(MonthLong),
206    #[xml(tag = "w:yearLong")]
207    YearLong(YearLong),
208    #[xml(tag = "w:annotationRef")]
209    AnnotationRef(AnnotationRef),
210    #[xml(tag = "w:footnoteRef")]
211    FootnoteRef(FootnoteRef),
212    #[xml(tag = "w:endnoteRef")]
213    EndnoteRef(EndnoteRef),
214    #[xml(tag = "w:separator")]
215    Separator(Separator),
216    #[xml(tag = "w:continuationSeparator")]
217    ContinuationSeparator(ContinuationSeparator),
218    #[xml(tag = "w:sym")]
219    Sym(Sym<'a>),
220    #[xml(tag = "w:pgNum")]
221    PgNum(PgNum),
222    #[xml(tag = "w:cr")]
223    CarriageReturn(CarriageReturn),
224    #[xml(tag = "w:tab")]
225    Tab(Tab),
226    //#[xml(tag = "w:object")]
227    //Object(Object<'a>),
228    #[xml(tag = "w:pict")]
229    Pict(Pict<'a>),
230    #[xml(tag = "w:fldChar")]
231    FieldChar(FieldChar),
232    //#[xml(tag = "w:ruby")]
233    //Ruby(Ruby<'a>),
234    #[xml(tag = "w:footnoteReference")]
235    FootnoteReference(FootnoteReference<'a>),
236    #[xml(tag = "w:endnoteReference")]
237    EndnoteReference(EndnoteReference<'a>),
238    #[xml(tag = "w:commentReference")]
239    CommentReference(CommentReference<'a>),
240    #[xml(tag = "w:drawing")]
241    Drawing(Drawing<'a>),
242    #[xml(tag = "w:ptab")]
243    PTab(PTab),
244    #[xml(tag = "w:lastRenderedPageBreak")]
245    LastRenderedPageBreak(LastRenderedPageBreak),
246}
247
248__define_struct! {
249    ("w:ptab", PTab) {
250        "w:alignment", alignment,	PTabAlignment	//Positional Tab Stop Alignment
251        "w:relativeTo",	relative_to,	PTabRelativeTo	//Positional Tab Base
252        "w:leader",	leader,	PTabLeader	//Tab Leader Character
253    }
254}
255
256__define_enum! {
257    PTabAlignment {
258        Left = "left", // Left
259        Center = "center", // Center
260        Right = "right", // Right
261    }
262}
263
264__define_enum! {
265    PTabRelativeTo {
266        Margin = "margin", // Relative To Text Margins
267        Indent = "indent", // Relative To Indents
268    }
269}
270
271__define_enum! {
272    PTabLeader {
273        None = "none", // No Leader Character
274        Dot = "dot", // Dot Leader Character
275        Hyphen = "hyphen", // Hyphen Leader Character
276        Underscore = "underscore", // Underscore Leader Character
277        MiddleDot = "middleDot", // Centered Dot Leader Character
278    }
279}
280
281#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
282#[cfg_attr(test, derive(PartialEq))]
283#[xml(tag = "w:noBreakHyphen")]
284pub struct NoBreakHyphen;
285
286#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
287#[cfg_attr(test, derive(PartialEq))]
288#[xml(tag = "w:softHyphen")]
289pub struct SoftHyphen {}
290
291#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
292#[cfg_attr(test, derive(PartialEq))]
293#[xml(tag = "w:separator")]
294pub struct Separator {}
295
296#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
297#[cfg_attr(test, derive(PartialEq))]
298#[xml(tag = "w:continuationSeparator")]
299pub struct ContinuationSeparator {}
300
301#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
302#[cfg_attr(test, derive(PartialEq))]
303#[xml(tag = "w:pgNum")]
304pub struct PgNum {}
305
306__xml_test_suites!(
307    Run,
308    Run::default(),
309    r#"<w:r/>"#,
310    Run::default().push_break(None),
311    r#"<w:r><w:br/></w:r>"#,
312    Run::default().push_text("text"),
313    r#"<w:r><w:t>text</w:t></w:r>"#,
314);