1 | //! Contains high-level interface for an events-based XML emitter.
|
2 |
|
3 | use std::borrow::Cow;
|
4 | use std::io::{self, Write};
|
5 |
|
6 | use crate::encoding::UTF8_BOM;
|
7 | use crate::events::{attributes::Attribute, BytesCData, BytesPI, BytesStart, BytesText, Event};
|
8 |
|
9 | #[cfg (feature = "async-tokio" )]
|
10 | mod async_tokio;
|
11 |
|
12 | /// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] or [`tokio::io::AsyncWrite`] implementor.
|
13 | #[cfg (feature = "serialize" )]
|
14 | use {crate::se::SeError, serde::Serialize};
|
15 |
|
16 | /// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] implementor.
|
17 | ///
|
18 | /// # Examples
|
19 | ///
|
20 | /// ```
|
21 | /// # use pretty_assertions::assert_eq;
|
22 | /// use quick_xml::events::{Event, BytesEnd, BytesStart};
|
23 | /// use quick_xml::reader::Reader;
|
24 | /// use quick_xml::writer::Writer;
|
25 | /// use std::io::Cursor;
|
26 | ///
|
27 | /// let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"# ;
|
28 | /// let mut reader = Reader::from_str(xml);
|
29 | /// let mut writer = Writer::new(Cursor::new(Vec::new()));
|
30 | /// loop {
|
31 | /// match reader.read_event() {
|
32 | /// Ok(Event::Start(e)) if e.name().as_ref() == b"this_tag" => {
|
33 | ///
|
34 | /// // crates a new element ... alternatively we could reuse `e` by calling
|
35 | /// // `e.into_owned()`
|
36 | /// let mut elem = BytesStart::new("my_elem" );
|
37 | ///
|
38 | /// // collect existing attributes
|
39 | /// elem.extend_attributes(e.attributes().map(|attr| attr.unwrap()));
|
40 | ///
|
41 | /// // copy existing attributes, adds a new my-key="some value" attribute
|
42 | /// elem.push_attribute(("my-key" , "some value" ));
|
43 | ///
|
44 | /// // writes the event to the writer
|
45 | /// assert!(writer.write_event(Event::Start(elem)).is_ok());
|
46 | /// },
|
47 | /// Ok(Event::End(e)) if e.name().as_ref() == b"this_tag" => {
|
48 | /// assert!(writer.write_event(Event::End(BytesEnd::new("my_elem" ))).is_ok());
|
49 | /// },
|
50 | /// Ok(Event::Eof) => break,
|
51 | /// // we can either move or borrow the event to write, depending on your use-case
|
52 | /// Ok(e) => assert!(writer.write_event(e.borrow()).is_ok()),
|
53 | /// Err(e) => panic!("Error at position {}: {:?}" , reader.error_position(), e),
|
54 | /// }
|
55 | /// }
|
56 | ///
|
57 | /// let result = writer.into_inner().into_inner();
|
58 | /// let expected = r#"<my_elem k1="v1" k2="v2" my-key="some value"><child>text</child></my_elem>"# ;
|
59 | /// assert_eq!(result, expected.as_bytes());
|
60 | /// ```
|
61 | #[derive (Clone)]
|
62 | pub struct Writer<W> {
|
63 | /// underlying writer
|
64 | writer: W,
|
65 | indent: Option<Indentation>,
|
66 | }
|
67 |
|
68 | impl<W> Writer<W> {
|
69 | /// Creates a `Writer` from a generic writer.
|
70 | pub const fn new(inner: W) -> Writer<W> {
|
71 | Writer {
|
72 | writer: inner,
|
73 | indent: None,
|
74 | }
|
75 | }
|
76 |
|
77 | /// Creates a `Writer` with configured indents from a generic writer.
|
78 | pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
|
79 | Writer {
|
80 | writer: inner,
|
81 | indent: Some(Indentation::new(indent_char, indent_size)),
|
82 | }
|
83 | }
|
84 |
|
85 | /// Consumes this `Writer`, returning the underlying writer.
|
86 | pub fn into_inner(self) -> W {
|
87 | self.writer
|
88 | }
|
89 |
|
90 | /// Get a mutable reference to the underlying writer.
|
91 | pub fn get_mut(&mut self) -> &mut W {
|
92 | &mut self.writer
|
93 | }
|
94 |
|
95 | /// Get a reference to the underlying writer.
|
96 | pub const fn get_ref(&self) -> &W {
|
97 | &self.writer
|
98 | }
|
99 |
|
100 | /// Provides a simple, high-level API for writing XML elements.
|
101 | ///
|
102 | /// Returns an [`ElementWriter`] that simplifies setting attributes and writing
|
103 | /// content inside the element.
|
104 | ///
|
105 | /// # Example
|
106 | ///
|
107 | /// ```
|
108 | /// # use quick_xml::Result;
|
109 | /// # fn main() -> Result<()> {
|
110 | /// use quick_xml::events::{BytesStart, BytesText, Event};
|
111 | /// use quick_xml::writer::Writer;
|
112 | /// use quick_xml::Error;
|
113 | /// use std::io::Cursor;
|
114 | ///
|
115 | /// let mut writer = Writer::new(Cursor::new(Vec::new()));
|
116 | ///
|
117 | /// // writes <tag attr1="value1"/>
|
118 | /// writer.create_element("tag" )
|
119 | /// .with_attribute(("attr1" , "value1" )) // chain `with_attribute()` calls to add many attributes
|
120 | /// .write_empty()?;
|
121 | ///
|
122 | /// // writes <tag attr1="value1" attr2="value2">with some text inside</tag>
|
123 | /// writer.create_element("tag" )
|
124 | /// .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter()) // or add attributes from an iterator
|
125 | /// .write_text_content(BytesText::new("with some text inside" ))?;
|
126 | ///
|
127 | /// // writes <tag><fruit quantity="0">apple</fruit><fruit quantity="1">orange</fruit></tag>
|
128 | /// writer.create_element("tag" )
|
129 | /// // We need to provide error type, because it is not named somewhere explicitly
|
130 | /// .write_inner_content(|writer| {
|
131 | /// let fruits = ["apple" , "orange" ];
|
132 | /// for (quant, item) in fruits.iter().enumerate() {
|
133 | /// writer
|
134 | /// .create_element("fruit" )
|
135 | /// .with_attribute(("quantity" , quant.to_string().as_str()))
|
136 | /// .write_text_content(BytesText::new(item))?;
|
137 | /// }
|
138 | /// Ok(())
|
139 | /// })?;
|
140 | /// # Ok(())
|
141 | /// # }
|
142 | /// ```
|
143 | #[must_use ]
|
144 | pub fn create_element<'a, N>(&'a mut self, name: N) -> ElementWriter<'a, W>
|
145 | where
|
146 | N: Into<Cow<'a, str>>,
|
147 | {
|
148 | ElementWriter {
|
149 | writer: self,
|
150 | start_tag: BytesStart::new(name),
|
151 | state: AttributeIndent::NoneAttributesWritten,
|
152 | spaces: Vec::new(),
|
153 | }
|
154 | }
|
155 | }
|
156 |
|
157 | impl<W: Write> Writer<W> {
|
158 | /// Write a [Byte-Order-Mark] character to the document.
|
159 | ///
|
160 | /// # Example
|
161 | ///
|
162 | /// ```rust
|
163 | /// # use quick_xml::Result;
|
164 | /// # fn main() -> Result<()> {
|
165 | /// use quick_xml::events::{BytesStart, BytesText, Event};
|
166 | /// use quick_xml::writer::Writer;
|
167 | /// use quick_xml::Error;
|
168 | /// use std::io::Cursor;
|
169 | ///
|
170 | /// let mut buffer = Vec::new();
|
171 | /// let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
172 | ///
|
173 | /// writer.write_bom()?;
|
174 | /// writer
|
175 | /// .create_element("empty" )
|
176 | /// .with_attribute(("attr1" , "value1" ))
|
177 | /// .write_empty()
|
178 | /// .expect("failure" );
|
179 | ///
|
180 | /// assert_eq!(
|
181 | /// std::str::from_utf8(&buffer).unwrap(),
|
182 | /// " \u{FEFF}<empty attr1= \"value1 \"/>"
|
183 | /// );
|
184 | /// # Ok(())
|
185 | /// # }
|
186 | /// ```
|
187 | /// [Byte-Order-Mark]: https://unicode.org/faq/utf_bom.html#BOM
|
188 | pub fn write_bom(&mut self) -> io::Result<()> {
|
189 | self.write(UTF8_BOM)
|
190 | }
|
191 |
|
192 | /// Writes the given event to the underlying writer.
|
193 | pub fn write_event<'a, E: Into<Event<'a>>>(&mut self, event: E) -> io::Result<()> {
|
194 | let mut next_should_line_break = true;
|
195 | let result = match event.into() {
|
196 | Event::Start(e) => {
|
197 | let result = self.write_wrapped(b"<" , &e, b">" );
|
198 | if let Some(i) = self.indent.as_mut() {
|
199 | i.grow();
|
200 | }
|
201 | result
|
202 | }
|
203 | Event::End(e) => {
|
204 | if let Some(i) = self.indent.as_mut() {
|
205 | i.shrink();
|
206 | }
|
207 | self.write_wrapped(b"</" , &e, b">" )
|
208 | }
|
209 | Event::Empty(e) => self.write_wrapped(b"<" , &e, b"/>" ),
|
210 | Event::Text(e) => {
|
211 | next_should_line_break = false;
|
212 | self.write(&e)
|
213 | }
|
214 | Event::Comment(e) => self.write_wrapped(b"<!--" , &e, b"-->" ),
|
215 | Event::CData(e) => {
|
216 | next_should_line_break = false;
|
217 | self.write(b"<![CDATA[" )?;
|
218 | self.write(&e)?;
|
219 | self.write(b"]]>" )
|
220 | }
|
221 | Event::Decl(e) => self.write_wrapped(b"<?" , &e, b"?>" ),
|
222 | Event::PI(e) => self.write_wrapped(b"<?" , &e, b"?>" ),
|
223 | Event::DocType(e) => self.write_wrapped(b"<!DOCTYPE " , &e, b">" ),
|
224 | Event::Eof => Ok(()),
|
225 | };
|
226 | if let Some(i) = self.indent.as_mut() {
|
227 | i.should_line_break = next_should_line_break;
|
228 | }
|
229 | result
|
230 | }
|
231 |
|
232 | /// Writes bytes
|
233 | #[inline ]
|
234 | pub(crate) fn write(&mut self, value: &[u8]) -> io::Result<()> {
|
235 | self.writer.write_all(value).map_err(Into::into)
|
236 | }
|
237 |
|
238 | #[inline ]
|
239 | fn write_wrapped(&mut self, before: &[u8], value: &[u8], after: &[u8]) -> io::Result<()> {
|
240 | if let Some(ref i) = self.indent {
|
241 | if i.should_line_break {
|
242 | self.writer.write_all(b" \n" )?;
|
243 | self.writer.write_all(i.current())?;
|
244 | }
|
245 | }
|
246 | self.write(before)?;
|
247 | self.write(value)?;
|
248 | self.write(after)?;
|
249 | Ok(())
|
250 | }
|
251 |
|
252 | /// Manually write a newline and indentation at the proper level.
|
253 | ///
|
254 | /// This can be used when the heuristic to line break and indent after any
|
255 | /// [`Event`] apart from [`Text`] fails such as when a [`Start`] occurs directly
|
256 | /// after [`Text`].
|
257 | ///
|
258 | /// This method will do nothing if `Writer` was not constructed with [`new_with_indent`].
|
259 | ///
|
260 | /// [`Text`]: Event::Text
|
261 | /// [`Start`]: Event::Start
|
262 | /// [`new_with_indent`]: Self::new_with_indent
|
263 | pub fn write_indent(&mut self) -> io::Result<()> {
|
264 | if let Some(ref i) = self.indent {
|
265 | self.writer.write_all(b" \n" )?;
|
266 | self.writer.write_all(i.current())?;
|
267 | }
|
268 | Ok(())
|
269 | }
|
270 |
|
271 | /// Write an arbitrary serializable type
|
272 | ///
|
273 | /// Note: If you are attempting to write XML in a non-UTF-8 encoding, this may not
|
274 | /// be safe to use. Rust basic types assume UTF-8 encodings.
|
275 | ///
|
276 | /// ```rust
|
277 | /// # use pretty_assertions::assert_eq;
|
278 | /// # use serde::Serialize;
|
279 | /// # use quick_xml::events::{BytesStart, Event};
|
280 | /// # use quick_xml::writer::Writer;
|
281 | /// # use quick_xml::se::SeError;
|
282 | /// # fn main() -> Result<(), SeError> {
|
283 | /// #[derive(Debug, PartialEq, Serialize)]
|
284 | /// struct MyData {
|
285 | /// question: String,
|
286 | /// answer: u32,
|
287 | /// }
|
288 | ///
|
289 | /// let data = MyData {
|
290 | /// question: "The Ultimate Question of Life, the Universe, and Everything".into(),
|
291 | /// answer: 42,
|
292 | /// };
|
293 | ///
|
294 | /// let mut buffer = Vec::new();
|
295 | /// let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
|
296 | ///
|
297 | /// let start = BytesStart::new("root");
|
298 | /// let end = start.to_end();
|
299 | ///
|
300 | /// writer.write_event(Event::Start(start.clone()))?;
|
301 | /// writer.write_serializable("my_data", &data)?;
|
302 | /// writer.write_event(Event::End(end))?;
|
303 | ///
|
304 | /// assert_eq!(
|
305 | /// std::str::from_utf8(&buffer)?,
|
306 | /// r#"<root>
|
307 | /// <my_data>
|
308 | /// <question>The Ultimate Question of Life, the Universe, and Everything</question>
|
309 | /// <answer>42</answer>
|
310 | /// </my_data>
|
311 | /// </root>"#
|
312 | /// );
|
313 | /// # Ok(())
|
314 | /// # }
|
315 | /// ```
|
316 | #[cfg (feature = "serialize" )]
|
317 | pub fn write_serializable<T: Serialize>(
|
318 | &mut self,
|
319 | tag_name: &str,
|
320 | content: &T,
|
321 | ) -> Result<(), SeError> {
|
322 | use crate::se::{Indent, Serializer};
|
323 |
|
324 | self.write_indent()?;
|
325 | let mut fmt = ToFmtWrite(&mut self.writer);
|
326 | let mut serializer = Serializer::with_root(&mut fmt, Some(tag_name))?;
|
327 |
|
328 | if let Some(indent) = &mut self.indent {
|
329 | serializer.set_indent(Indent::Borrow(indent));
|
330 | }
|
331 |
|
332 | content.serialize(serializer)?;
|
333 |
|
334 | Ok(())
|
335 | }
|
336 | }
|
337 |
|
338 | /// Track indent inside elements state
|
339 | ///
|
340 | /// ```mermaid
|
341 | /// stateDiagram-v2
|
342 | /// [*] --> NoneAttributesWritten
|
343 | /// NoneAttributesWritten --> Spaces : .with_attribute()
|
344 | /// NoneAttributesWritten --> WriteConfigured : .new_line()
|
345 | ///
|
346 | /// Spaces --> Spaces : .with_attribute()
|
347 | /// Spaces --> WriteSpaces : .new_line()
|
348 | ///
|
349 | /// WriteSpaces --> Spaces : .with_attribute()
|
350 | /// WriteSpaces --> WriteSpaces : .new_line()
|
351 | ///
|
352 | /// Configured --> Configured : .with_attribute()
|
353 | /// Configured --> WriteConfigured : .new_line()
|
354 | ///
|
355 | /// WriteConfigured --> Configured : .with_attribute()
|
356 | /// WriteConfigured --> WriteConfigured : .new_line()
|
357 | /// ```
|
358 | #[derive (Debug)]
|
359 | enum AttributeIndent {
|
360 | /// Initial state. `ElementWriter` was just created and no attributes written yet
|
361 | NoneAttributesWritten,
|
362 | /// Write specified count of spaces to indent before writing attribute in `with_attribute()`
|
363 | WriteSpaces(usize),
|
364 | /// Keep space indent that should be used if `new_line()` would be called
|
365 | Spaces(usize),
|
366 | /// Write specified count of indent characters before writing attribute in `with_attribute()`
|
367 | WriteConfigured(usize),
|
368 | /// Keep indent that should be used if `new_line()` would be called
|
369 | Configured(usize),
|
370 | }
|
371 |
|
372 | /// A struct to write an element. Contains methods to add attributes and inner
|
373 | /// elements to the element
|
374 | pub struct ElementWriter<'a, W> {
|
375 | writer: &'a mut Writer<W>,
|
376 | start_tag: BytesStart<'a>,
|
377 | state: AttributeIndent,
|
378 | /// Contains spaces used to write space indents of attributes
|
379 | spaces: Vec<u8>,
|
380 | }
|
381 |
|
382 | impl<'a, W> ElementWriter<'a, W> {
|
383 | /// Adds an attribute to this element.
|
384 | pub fn with_attribute<'b, I>(mut self, attr: I) -> Self
|
385 | where
|
386 | I: Into<Attribute<'b>>,
|
387 | {
|
388 | self.write_attr(attr.into());
|
389 | self
|
390 | }
|
391 |
|
392 | /// Add additional attributes to this element using an iterator.
|
393 | ///
|
394 | /// The yielded items must be convertible to [`Attribute`] using `Into`.
|
395 | pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
|
396 | where
|
397 | I: IntoIterator,
|
398 | I::Item: Into<Attribute<'b>>,
|
399 | {
|
400 | let mut iter = attributes.into_iter();
|
401 | if let Some(attr) = iter.next() {
|
402 | self.write_attr(attr.into());
|
403 | self.start_tag.extend_attributes(iter);
|
404 | }
|
405 | self
|
406 | }
|
407 |
|
408 | /// Push a new line inside an element between attributes. Note, that this
|
409 | /// method does nothing if [`Writer`] was created without indentation support.
|
410 | ///
|
411 | /// # Examples
|
412 | ///
|
413 | /// The following code
|
414 | ///
|
415 | /// ```
|
416 | /// # use quick_xml::writer::Writer;
|
417 | /// let mut buffer = Vec::new();
|
418 | /// let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 2);
|
419 | /// writer
|
420 | /// .create_element("element" )
|
421 | /// //.new_line() (1)
|
422 | /// .with_attribute(("first" , "1" ))
|
423 | /// .with_attribute(("second" , "2" ))
|
424 | /// .new_line()
|
425 | /// .with_attributes([
|
426 | /// ("third" , "3" ),
|
427 | /// ("fourth" , "4" ),
|
428 | /// ])
|
429 | /// //.new_line() (2)
|
430 | /// .write_empty();
|
431 | /// ```
|
432 | /// will produce the following XMLs:
|
433 | /// ```xml
|
434 | /// <!-- result of the code above. Spaces always is used -->
|
435 | /// <element first="1" second="2"
|
436 | /// third="3" fourth="4"/>
|
437 | ///
|
438 | /// <!-- if uncomment only (1) - indent depends on indentation
|
439 | /// settings - 2 spaces here -->
|
440 | /// <element
|
441 | /// first="1" second="2"
|
442 | /// third="3" fourth="4"/>
|
443 | ///
|
444 | /// <!-- if uncomment only (2). Spaces always is used -->
|
445 | /// <element first="1" second="2"
|
446 | /// third="3" fourth="4"
|
447 | /// />
|
448 | /// ```
|
449 | pub fn new_line(mut self) -> Self {
|
450 | if let Some(i) = self.writer.indent.as_mut() {
|
451 | match self.state {
|
452 | // .new_line() called just after .create_element().
|
453 | // Use element indent to additionally indent attributes
|
454 | AttributeIndent::NoneAttributesWritten => {
|
455 | self.state = AttributeIndent::WriteConfigured(i.indent_size)
|
456 | }
|
457 |
|
458 | AttributeIndent::WriteSpaces(_) => {}
|
459 | // .new_line() called when .with_attribute() was called at least once.
|
460 | // The spaces should be used to indent
|
461 | // Plan saved indent
|
462 | AttributeIndent::Spaces(indent) => {
|
463 | self.state = AttributeIndent::WriteSpaces(indent)
|
464 | }
|
465 |
|
466 | AttributeIndent::WriteConfigured(_) => {}
|
467 | // .new_line() called when .with_attribute() was called at least once.
|
468 | // The configured indent characters should be used to indent
|
469 | // Plan saved indent
|
470 | AttributeIndent::Configured(indent) => {
|
471 | self.state = AttributeIndent::WriteConfigured(indent)
|
472 | }
|
473 | }
|
474 | self.start_tag.push_newline();
|
475 | };
|
476 | self
|
477 | }
|
478 |
|
479 | /// Writes attribute and maintain indentation state
|
480 | fn write_attr<'b>(&mut self, attr: Attribute<'b>) {
|
481 | if let Some(i) = self.writer.indent.as_mut() {
|
482 | // Save the indent that we should use next time when .new_line() be called
|
483 | self.state = match self.state {
|
484 | // Neither .new_line() or .with_attribute() yet called
|
485 | // If newline inside attributes will be requested, we should indent them
|
486 | // by the length of tag name and +1 for `<` and +1 for one space
|
487 | AttributeIndent::NoneAttributesWritten => {
|
488 | self.start_tag.push_attribute(attr);
|
489 | AttributeIndent::Spaces(self.start_tag.name().as_ref().len() + 2)
|
490 | }
|
491 |
|
492 | // Indent was requested by previous call to .new_line(), write it
|
493 | // New line was already written
|
494 | AttributeIndent::WriteSpaces(indent) => {
|
495 | if self.spaces.len() < indent {
|
496 | self.spaces.resize(indent, b' ' );
|
497 | }
|
498 | self.start_tag.push_indent(&self.spaces[..indent]);
|
499 | self.start_tag.push_attr(attr.into());
|
500 | AttributeIndent::Spaces(indent)
|
501 | }
|
502 | // .new_line() was not called, but .with_attribute() was.
|
503 | // use the previously calculated indent
|
504 | AttributeIndent::Spaces(indent) => {
|
505 | self.start_tag.push_attribute(attr);
|
506 | AttributeIndent::Spaces(indent)
|
507 | }
|
508 |
|
509 | // Indent was requested by previous call to .new_line(), write it
|
510 | // New line was already written
|
511 | AttributeIndent::WriteConfigured(indent) => {
|
512 | self.start_tag.push_indent(i.additional(indent));
|
513 | self.start_tag.push_attr(attr.into());
|
514 | AttributeIndent::Configured(indent)
|
515 | }
|
516 | // .new_line() was not called, but .with_attribute() was.
|
517 | // use the previously calculated indent
|
518 | AttributeIndent::Configured(indent) => {
|
519 | self.start_tag.push_attribute(attr);
|
520 | AttributeIndent::Configured(indent)
|
521 | }
|
522 | };
|
523 | } else {
|
524 | self.start_tag.push_attribute(attr);
|
525 | }
|
526 | }
|
527 | }
|
528 |
|
529 | impl<'a, W: Write> ElementWriter<'a, W> {
|
530 | /// Write some text inside the current element.
|
531 | pub fn write_text_content(self, text: BytesText) -> io::Result<&'a mut Writer<W>> {
|
532 | self.writer
|
533 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
534 | self.writer.write_event(Event::Text(text))?;
|
535 | self.writer
|
536 | .write_event(Event::End(self.start_tag.to_end()))?;
|
537 | Ok(self.writer)
|
538 | }
|
539 |
|
540 | /// Write a CData event `<![CDATA[...]]>` inside the current element.
|
541 | pub fn write_cdata_content(self, text: BytesCData) -> io::Result<&'a mut Writer<W>> {
|
542 | self.writer
|
543 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
544 | self.writer.write_event(Event::CData(text))?;
|
545 | self.writer
|
546 | .write_event(Event::End(self.start_tag.to_end()))?;
|
547 | Ok(self.writer)
|
548 | }
|
549 |
|
550 | /// Write a processing instruction `<?...?>` inside the current element.
|
551 | pub fn write_pi_content(self, pi: BytesPI) -> io::Result<&'a mut Writer<W>> {
|
552 | self.writer
|
553 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
554 | self.writer.write_event(Event::PI(pi))?;
|
555 | self.writer
|
556 | .write_event(Event::End(self.start_tag.to_end()))?;
|
557 | Ok(self.writer)
|
558 | }
|
559 |
|
560 | /// Write an empty (self-closing) tag.
|
561 | pub fn write_empty(self) -> io::Result<&'a mut Writer<W>> {
|
562 | self.writer.write_event(Event::Empty(self.start_tag))?;
|
563 | Ok(self.writer)
|
564 | }
|
565 |
|
566 | /// Create a new scope for writing XML inside the current element.
|
567 | pub fn write_inner_content<F>(self, closure: F) -> io::Result<&'a mut Writer<W>>
|
568 | where
|
569 | F: FnOnce(&mut Writer<W>) -> io::Result<()>,
|
570 | {
|
571 | self.writer
|
572 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
573 | closure(self.writer)?;
|
574 | self.writer
|
575 | .write_event(Event::End(self.start_tag.to_end()))?;
|
576 | Ok(self.writer)
|
577 | }
|
578 | }
|
579 | #[cfg (feature = "serialize" )]
|
580 | pub(crate) struct ToFmtWrite<T>(pub T);
|
581 |
|
582 | #[cfg (feature = "serialize" )]
|
583 | impl<T> std::fmt::Write for ToFmtWrite<T>
|
584 | where
|
585 | T: std::io::Write,
|
586 | {
|
587 | fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
588 | self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
|
589 | }
|
590 | }
|
591 |
|
592 | #[derive (Clone)]
|
593 | pub(crate) struct Indentation {
|
594 | /// todo: this is an awkward fit as it has no impact on indentation logic, but it is
|
595 | /// only applicable when an indentation exists. Potentially refactor later
|
596 | should_line_break: bool,
|
597 | /// The character code to be used for indentations (e.g. ` ` or `\t`)
|
598 | indent_char: u8,
|
599 | /// How many instances of the indent character ought to be used for each level of indentation
|
600 | indent_size: usize,
|
601 | /// Used as a cache for the bytes used for indentation
|
602 | indents: Vec<u8>,
|
603 | /// The current amount of indentation
|
604 | current_indent_len: usize,
|
605 | }
|
606 |
|
607 | impl Indentation {
|
608 | pub fn new(indent_char: u8, indent_size: usize) -> Self {
|
609 | Self {
|
610 | should_line_break: false,
|
611 | indent_char,
|
612 | indent_size,
|
613 | indents: vec![indent_char; 128],
|
614 | current_indent_len: 0, // invariant - needs to remain less than indents.len()
|
615 | }
|
616 | }
|
617 |
|
618 | /// Increase indentation by one level
|
619 | pub fn grow(&mut self) {
|
620 | self.current_indent_len += self.indent_size;
|
621 | self.ensure(self.current_indent_len);
|
622 | }
|
623 |
|
624 | /// Decrease indentation by one level. Do nothing, if level already zero
|
625 | pub fn shrink(&mut self) {
|
626 | self.current_indent_len = self.current_indent_len.saturating_sub(self.indent_size);
|
627 | }
|
628 |
|
629 | /// Returns indent string for current level
|
630 | pub fn current(&self) -> &[u8] {
|
631 | &self.indents[..self.current_indent_len]
|
632 | }
|
633 |
|
634 | /// Returns indent with current indent plus additional indent
|
635 | pub fn additional(&mut self, additional_indent: usize) -> &[u8] {
|
636 | let new_len = self.current_indent_len + additional_indent;
|
637 | self.ensure(new_len);
|
638 | &self.indents[..new_len]
|
639 | }
|
640 |
|
641 | fn ensure(&mut self, new_len: usize) {
|
642 | if self.indents.len() < new_len {
|
643 | self.indents.resize(new_len, self.indent_char);
|
644 | }
|
645 | }
|
646 | }
|
647 | |