1 | //! Contains high-level interface for an events-based XML emitter.
|
2 |
|
3 | use std::io::Write;
|
4 |
|
5 | use crate::encoding::UTF8_BOM;
|
6 | use crate::errors::Result;
|
7 | use crate::events::{attributes::Attribute, BytesCData, 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::de::DeError, 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 | /// reader.trim_text(true);
|
30 | /// let mut writer = Writer::new(Cursor::new(Vec::new()));
|
31 | /// loop {
|
32 | /// match reader.read_event() {
|
33 | /// Ok(Event::Start(e)) if e.name().as_ref() == b"this_tag" => {
|
34 | ///
|
35 | /// // crates a new element ... alternatively we could reuse `e` by calling
|
36 | /// // `e.into_owned()`
|
37 | /// let mut elem = BytesStart::new("my_elem" );
|
38 | ///
|
39 | /// // collect existing attributes
|
40 | /// elem.extend_attributes(e.attributes().map(|attr| attr.unwrap()));
|
41 | ///
|
42 | /// // copy existing attributes, adds a new my-key="some value" attribute
|
43 | /// elem.push_attribute(("my-key" , "some value" ));
|
44 | ///
|
45 | /// // writes the event to the writer
|
46 | /// assert!(writer.write_event(Event::Start(elem)).is_ok());
|
47 | /// },
|
48 | /// Ok(Event::End(e)) if e.name().as_ref() == b"this_tag" => {
|
49 | /// assert!(writer.write_event(Event::End(BytesEnd::new("my_elem" ))).is_ok());
|
50 | /// },
|
51 | /// Ok(Event::Eof) => break,
|
52 | /// // we can either move or borrow the event to write, depending on your use-case
|
53 | /// Ok(e) => assert!(writer.write_event(e).is_ok()),
|
54 | /// Err(e) => panic!("Error at position {}: {:?}" , reader.buffer_position(), e),
|
55 | /// }
|
56 | /// }
|
57 | ///
|
58 | /// let result = writer.into_inner().into_inner();
|
59 | /// let expected = r#"<my_elem k1="v1" k2="v2" my-key="some value"><child>text</child></my_elem>"# ;
|
60 | /// assert_eq!(result, expected.as_bytes());
|
61 | /// ```
|
62 | #[derive (Clone)]
|
63 | pub struct Writer<W> {
|
64 | /// underlying writer
|
65 | writer: W,
|
66 | indent: Option<Indentation>,
|
67 | }
|
68 |
|
69 | impl<W> Writer<W> {
|
70 | /// Creates a `Writer` from a generic writer.
|
71 | pub fn new(inner: W) -> Writer<W> {
|
72 | Writer {
|
73 | writer: inner,
|
74 | indent: None,
|
75 | }
|
76 | }
|
77 |
|
78 | /// Creates a `Writer` with configured indents from a generic writer.
|
79 | pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
|
80 | Writer {
|
81 | writer: inner,
|
82 | indent: Some(Indentation::new(indent_char, indent_size)),
|
83 | }
|
84 | }
|
85 |
|
86 | /// Consumes this `Writer`, returning the underlying writer.
|
87 | pub fn into_inner(self) -> W {
|
88 | self.writer
|
89 | }
|
90 |
|
91 | /// Get a mutable reference to the underlying writer.
|
92 | pub fn get_mut(&mut self) -> &mut W {
|
93 | &mut self.writer
|
94 | }
|
95 |
|
96 | /// Get a reference to the underlying writer.
|
97 | pub fn get_ref(&self) -> &W {
|
98 | &self.writer
|
99 | }
|
100 | }
|
101 |
|
102 | impl<W: Write> Writer<W> {
|
103 | /// Write a [Byte-Order-Mark] character to the document.
|
104 | ///
|
105 | /// # Example
|
106 | ///
|
107 | /// ```rust
|
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 buffer = Vec::new();
|
116 | /// let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
117 | ///
|
118 | /// writer.write_bom()?;
|
119 | /// writer
|
120 | /// .create_element("empty" )
|
121 | /// .with_attribute(("attr1" , "value1" ))
|
122 | /// .write_empty()
|
123 | /// .expect("failure" );
|
124 | ///
|
125 | /// assert_eq!(
|
126 | /// std::str::from_utf8(&buffer).unwrap(),
|
127 | /// " \u{FEFF}<empty attr1= \"value1 \"/>"
|
128 | /// );
|
129 | /// # Ok(())
|
130 | /// # }
|
131 | /// ```
|
132 | /// [Byte-Order-Mark]: https://unicode.org/faq/utf_bom.html#BOM
|
133 | pub fn write_bom(&mut self) -> Result<()> {
|
134 | self.write(UTF8_BOM)
|
135 | }
|
136 |
|
137 | /// Writes the given event to the underlying writer.
|
138 | pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
|
139 | let mut next_should_line_break = true;
|
140 | let result = match *event.as_ref() {
|
141 | Event::Start(ref e) => {
|
142 | let result = self.write_wrapped(b"<" , e, b">" );
|
143 | if let Some(i) = self.indent.as_mut() {
|
144 | i.grow();
|
145 | }
|
146 | result
|
147 | }
|
148 | Event::End(ref e) => {
|
149 | if let Some(i) = self.indent.as_mut() {
|
150 | i.shrink();
|
151 | }
|
152 | self.write_wrapped(b"</" , e, b">" )
|
153 | }
|
154 | Event::Empty(ref e) => self.write_wrapped(b"<" , e, b"/>" ),
|
155 | Event::Text(ref e) => {
|
156 | next_should_line_break = false;
|
157 | self.write(e)
|
158 | }
|
159 | Event::Comment(ref e) => self.write_wrapped(b"<!--" , e, b"-->" ),
|
160 | Event::CData(ref e) => {
|
161 | next_should_line_break = false;
|
162 | self.write(b"<![CDATA[" )?;
|
163 | self.write(e)?;
|
164 | self.write(b"]]>" )
|
165 | }
|
166 | Event::Decl(ref e) => self.write_wrapped(b"<?" , e, b"?>" ),
|
167 | Event::PI(ref e) => self.write_wrapped(b"<?" , e, b"?>" ),
|
168 | Event::DocType(ref e) => self.write_wrapped(b"<!DOCTYPE " , e, b">" ),
|
169 | Event::Eof => Ok(()),
|
170 | };
|
171 | if let Some(i) = self.indent.as_mut() {
|
172 | i.should_line_break = next_should_line_break;
|
173 | }
|
174 | result
|
175 | }
|
176 |
|
177 | /// Writes bytes
|
178 | #[inline ]
|
179 | pub(crate) fn write(&mut self, value: &[u8]) -> Result<()> {
|
180 | self.writer.write_all(value).map_err(Into::into)
|
181 | }
|
182 |
|
183 | #[inline ]
|
184 | fn write_wrapped(&mut self, before: &[u8], value: &[u8], after: &[u8]) -> Result<()> {
|
185 | if let Some(ref i) = self.indent {
|
186 | if i.should_line_break {
|
187 | self.writer.write_all(b" \n" )?;
|
188 | self.writer.write_all(i.current())?;
|
189 | }
|
190 | }
|
191 | self.write(before)?;
|
192 | self.write(value)?;
|
193 | self.write(after)?;
|
194 | Ok(())
|
195 | }
|
196 |
|
197 | /// Manually write a newline and indentation at the proper level.
|
198 | ///
|
199 | /// This can be used when the heuristic to line break and indent after any
|
200 | /// [`Event`] apart from [`Text`] fails such as when a [`Start`] occurs directly
|
201 | /// after [`Text`].
|
202 | ///
|
203 | /// This method will do nothing if `Writer` was not constructed with [`new_with_indent`].
|
204 | ///
|
205 | /// [`Text`]: Event::Text
|
206 | /// [`Start`]: Event::Start
|
207 | /// [`new_with_indent`]: Self::new_with_indent
|
208 | pub fn write_indent(&mut self) -> Result<()> {
|
209 | if let Some(ref i) = self.indent {
|
210 | self.writer.write_all(b" \n" )?;
|
211 | self.writer.write_all(i.current())?;
|
212 | }
|
213 | Ok(())
|
214 | }
|
215 |
|
216 | /// Provides a simple, high-level API for writing XML elements.
|
217 | ///
|
218 | /// Returns an [`ElementWriter`] that simplifies setting attributes and writing
|
219 | /// content inside the element.
|
220 | ///
|
221 | /// # Example
|
222 | ///
|
223 | /// ```rust
|
224 | /// # use quick_xml::Result;
|
225 | /// # fn main() -> Result<()> {
|
226 | /// use quick_xml::events::{BytesStart, BytesText, Event};
|
227 | /// use quick_xml::writer::Writer;
|
228 | /// use quick_xml::Error;
|
229 | /// use std::io::Cursor;
|
230 | ///
|
231 | /// let mut writer = Writer::new(Cursor::new(Vec::new()));
|
232 | ///
|
233 | /// // writes <tag attr1="value1"/>
|
234 | /// writer.create_element("tag" )
|
235 | /// .with_attribute(("attr1" , "value1" )) // chain `with_attribute()` calls to add many attributes
|
236 | /// .write_empty()?;
|
237 | ///
|
238 | /// // writes <tag attr1="value1" attr2="value2">with some text inside</tag>
|
239 | /// writer.create_element("tag" )
|
240 | /// .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter()) // or add attributes from an iterator
|
241 | /// .write_text_content(BytesText::new("with some text inside" ))?;
|
242 | ///
|
243 | /// // writes <tag><fruit quantity="0">apple</fruit><fruit quantity="1">orange</fruit></tag>
|
244 | /// writer.create_element("tag" )
|
245 | /// .write_inner_content(|writer| {
|
246 | /// let fruits = ["apple" , "orange" ];
|
247 | /// for (quant, item) in fruits.iter().enumerate() {
|
248 | /// writer
|
249 | /// .create_element("fruit" )
|
250 | /// .with_attribute(("quantity" , quant.to_string().as_str()))
|
251 | /// .write_text_content(BytesText::new(item))?;
|
252 | /// }
|
253 | /// Ok(())
|
254 | /// })?;
|
255 | /// # Ok(())
|
256 | /// # }
|
257 | /// ```
|
258 | #[must_use ]
|
259 | pub fn create_element<'a, N>(&'a mut self, name: &'a N) -> ElementWriter<W>
|
260 | where
|
261 | N: 'a + AsRef<str> + ?Sized,
|
262 | {
|
263 | ElementWriter {
|
264 | writer: self,
|
265 | start_tag: BytesStart::new(name.as_ref()),
|
266 | }
|
267 | }
|
268 |
|
269 | /// Write an arbitrary serializable type
|
270 | ///
|
271 | /// Note: If you are attempting to write XML in a non-UTF-8 encoding, this may not
|
272 | /// be safe to use. Rust basic types assume UTF-8 encodings.
|
273 | ///
|
274 | /// ```rust
|
275 | /// # use pretty_assertions::assert_eq;
|
276 | /// # use serde::Serialize;
|
277 | /// # use quick_xml::events::{BytesStart, Event};
|
278 | /// # use quick_xml::writer::Writer;
|
279 | /// # use quick_xml::DeError;
|
280 | /// # fn main() -> Result<(), DeError> {
|
281 | /// #[derive(Debug, PartialEq, Serialize)]
|
282 | /// struct MyData {
|
283 | /// question: String,
|
284 | /// answer: u32,
|
285 | /// }
|
286 | ///
|
287 | /// let data = MyData {
|
288 | /// question: "The Ultimate Question of Life, the Universe, and Everything" .into(),
|
289 | /// answer: 42,
|
290 | /// };
|
291 | ///
|
292 | /// let mut buffer = Vec::new();
|
293 | /// let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
294 | ///
|
295 | /// let start = BytesStart::new("root" );
|
296 | /// let end = start.to_end();
|
297 | ///
|
298 | /// writer.write_event(Event::Start(start.clone()))?;
|
299 | /// writer.write_serializable("my_data" , &data)?;
|
300 | /// writer.write_event(Event::End(end))?;
|
301 | ///
|
302 | /// assert_eq!(
|
303 | /// std::str::from_utf8(&buffer)?,
|
304 | /// r#"<root>
|
305 | /// <my_data>
|
306 | /// <question>The Ultimate Question of Life, the Universe, and Everything</question>
|
307 | /// <answer>42</answer>
|
308 | /// </my_data>
|
309 | /// </root>"#
|
310 | /// );
|
311 | /// # Ok(())
|
312 | /// # }
|
313 | /// ```
|
314 | #[cfg (feature = "serialize" )]
|
315 | pub fn write_serializable<T: Serialize>(
|
316 | &mut self,
|
317 | tag_name: &str,
|
318 | content: &T,
|
319 | ) -> std::result::Result<(), DeError> {
|
320 | use crate::se::{Indent, Serializer};
|
321 |
|
322 | self.write_indent()?;
|
323 | let mut fmt = ToFmtWrite(&mut self.writer);
|
324 | let mut serializer = Serializer::with_root(&mut fmt, Some(tag_name))?;
|
325 |
|
326 | if let Some(indent) = &mut self.indent {
|
327 | serializer.set_indent(Indent::Borrow(indent));
|
328 | }
|
329 |
|
330 | content.serialize(serializer)?;
|
331 |
|
332 | Ok(())
|
333 | }
|
334 | }
|
335 |
|
336 | /// A struct to write an element. Contains methods to add attributes and inner
|
337 | /// elements to the element
|
338 | pub struct ElementWriter<'a, W: Write> {
|
339 | writer: &'a mut Writer<W>,
|
340 | start_tag: BytesStart<'a>,
|
341 | }
|
342 |
|
343 | impl<'a, W: Write> ElementWriter<'a, W> {
|
344 | /// Adds an attribute to this element.
|
345 | pub fn with_attribute<'b, I>(mut self, attr: I) -> Self
|
346 | where
|
347 | I: Into<Attribute<'b>>,
|
348 | {
|
349 | self.start_tag.push_attribute(attr);
|
350 | self
|
351 | }
|
352 |
|
353 | /// Add additional attributes to this element using an iterator.
|
354 | ///
|
355 | /// The yielded items must be convertible to [`Attribute`] using `Into`.
|
356 | pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
|
357 | where
|
358 | I: IntoIterator,
|
359 | I::Item: Into<Attribute<'b>>,
|
360 | {
|
361 | self.start_tag.extend_attributes(attributes);
|
362 | self
|
363 | }
|
364 |
|
365 | /// Write some text inside the current element.
|
366 | pub fn write_text_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
|
367 | self.writer
|
368 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
369 | self.writer.write_event(Event::Text(text))?;
|
370 | self.writer
|
371 | .write_event(Event::End(self.start_tag.to_end()))?;
|
372 | Ok(self.writer)
|
373 | }
|
374 |
|
375 | /// Write a CData event `<![CDATA[...]]>` inside the current element.
|
376 | pub fn write_cdata_content(self, text: BytesCData) -> Result<&'a mut Writer<W>> {
|
377 | self.writer
|
378 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
379 | self.writer.write_event(Event::CData(text))?;
|
380 | self.writer
|
381 | .write_event(Event::End(self.start_tag.to_end()))?;
|
382 | Ok(self.writer)
|
383 | }
|
384 |
|
385 | /// Write a processing instruction `<?...?>` inside the current element.
|
386 | pub fn write_pi_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
|
387 | self.writer
|
388 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
389 | self.writer.write_event(Event::PI(text))?;
|
390 | self.writer
|
391 | .write_event(Event::End(self.start_tag.to_end()))?;
|
392 | Ok(self.writer)
|
393 | }
|
394 |
|
395 | /// Write an empty (self-closing) tag.
|
396 | pub fn write_empty(self) -> Result<&'a mut Writer<W>> {
|
397 | self.writer.write_event(Event::Empty(self.start_tag))?;
|
398 | Ok(self.writer)
|
399 | }
|
400 |
|
401 | /// Create a new scope for writing XML inside the current element.
|
402 | pub fn write_inner_content<F>(self, closure: F) -> Result<&'a mut Writer<W>>
|
403 | where
|
404 | F: FnOnce(&mut Writer<W>) -> Result<()>,
|
405 | {
|
406 | self.writer
|
407 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
408 | closure(self.writer)?;
|
409 | self.writer
|
410 | .write_event(Event::End(self.start_tag.to_end()))?;
|
411 | Ok(self.writer)
|
412 | }
|
413 | }
|
414 | #[cfg (feature = "serialize" )]
|
415 | struct ToFmtWrite<T>(pub T);
|
416 |
|
417 | #[cfg (feature = "serialize" )]
|
418 | impl<T> std::fmt::Write for ToFmtWrite<T>
|
419 | where
|
420 | T: std::io::Write,
|
421 | {
|
422 | fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
423 | self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
|
424 | }
|
425 | }
|
426 |
|
427 | #[derive (Clone)]
|
428 | pub(crate) struct Indentation {
|
429 | /// todo: this is an awkward fit as it has no impact on indentation logic, but it is
|
430 | /// only applicable when an indentation exists. Potentially refactor later
|
431 | should_line_break: bool,
|
432 | /// The character code to be used for indentations (e.g. ` ` or `\t`)
|
433 | indent_char: u8,
|
434 | /// How many instances of the indent character ought to be used for each level of indentation
|
435 | indent_size: usize,
|
436 | /// Used as a cache for the bytes used for indentation
|
437 | indents: Vec<u8>,
|
438 | /// The current amount of indentation
|
439 | current_indent_len: usize,
|
440 | }
|
441 |
|
442 | impl Indentation {
|
443 | pub fn new(indent_char: u8, indent_size: usize) -> Self {
|
444 | Self {
|
445 | should_line_break: false,
|
446 | indent_char,
|
447 | indent_size,
|
448 | indents: vec![indent_char; 128],
|
449 | current_indent_len: 0, // invariant - needs to remain less than indents.len()
|
450 | }
|
451 | }
|
452 |
|
453 | /// Increase indentation by one level
|
454 | pub fn grow(&mut self) {
|
455 | self.current_indent_len += self.indent_size;
|
456 | if self.current_indent_len > self.indents.len() {
|
457 | self.indents
|
458 | .resize(self.current_indent_len, self.indent_char);
|
459 | }
|
460 | }
|
461 |
|
462 | /// Decrease indentation by one level. Do nothing, if level already zero
|
463 | pub fn shrink(&mut self) {
|
464 | self.current_indent_len = self.current_indent_len.saturating_sub(self.indent_size);
|
465 | }
|
466 |
|
467 | /// Returns indent string for current level
|
468 | pub fn current(&self) -> &[u8] {
|
469 | &self.indents[..self.current_indent_len]
|
470 | }
|
471 | }
|
472 |
|
473 | #[cfg (test)]
|
474 | mod indentation {
|
475 | use super::*;
|
476 | use crate::events::*;
|
477 | use pretty_assertions::assert_eq;
|
478 |
|
479 | #[test ]
|
480 | fn self_closed() {
|
481 | let mut buffer = Vec::new();
|
482 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
483 |
|
484 | let tag = BytesStart::new("self-closed" )
|
485 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
486 | writer
|
487 | .write_event(Event::Empty(tag))
|
488 | .expect("write tag failed" );
|
489 |
|
490 | assert_eq!(
|
491 | std::str::from_utf8(&buffer).unwrap(),
|
492 | r#"<self-closed attr1="value1" attr2="value2"/>"#
|
493 | );
|
494 | }
|
495 |
|
496 | #[test ]
|
497 | fn empty_paired() {
|
498 | let mut buffer = Vec::new();
|
499 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
500 |
|
501 | let start = BytesStart::new("paired" )
|
502 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
503 | let end = start.to_end();
|
504 | writer
|
505 | .write_event(Event::Start(start.clone()))
|
506 | .expect("write start tag failed" );
|
507 | writer
|
508 | .write_event(Event::End(end))
|
509 | .expect("write end tag failed" );
|
510 |
|
511 | assert_eq!(
|
512 | std::str::from_utf8(&buffer).unwrap(),
|
513 | r#"<paired attr1="value1" attr2="value2">
|
514 | </paired>"#
|
515 | );
|
516 | }
|
517 |
|
518 | #[test ]
|
519 | fn paired_with_inner() {
|
520 | let mut buffer = Vec::new();
|
521 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
522 |
|
523 | let start = BytesStart::new("paired" )
|
524 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
525 | let end = start.to_end();
|
526 | let inner = BytesStart::new("inner" );
|
527 |
|
528 | writer
|
529 | .write_event(Event::Start(start.clone()))
|
530 | .expect("write start tag failed" );
|
531 | writer
|
532 | .write_event(Event::Empty(inner))
|
533 | .expect("write inner tag failed" );
|
534 | writer
|
535 | .write_event(Event::End(end))
|
536 | .expect("write end tag failed" );
|
537 |
|
538 | assert_eq!(
|
539 | std::str::from_utf8(&buffer).unwrap(),
|
540 | r#"<paired attr1="value1" attr2="value2">
|
541 | <inner/>
|
542 | </paired>"#
|
543 | );
|
544 | }
|
545 |
|
546 | #[test ]
|
547 | fn paired_with_text() {
|
548 | let mut buffer = Vec::new();
|
549 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
550 |
|
551 | let start = BytesStart::new("paired" )
|
552 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
553 | let end = start.to_end();
|
554 | let text = BytesText::new("text" );
|
555 |
|
556 | writer
|
557 | .write_event(Event::Start(start.clone()))
|
558 | .expect("write start tag failed" );
|
559 | writer
|
560 | .write_event(Event::Text(text))
|
561 | .expect("write text failed" );
|
562 | writer
|
563 | .write_event(Event::End(end))
|
564 | .expect("write end tag failed" );
|
565 |
|
566 | assert_eq!(
|
567 | std::str::from_utf8(&buffer).unwrap(),
|
568 | r#"<paired attr1="value1" attr2="value2">text</paired>"#
|
569 | );
|
570 | }
|
571 |
|
572 | #[test ]
|
573 | fn mixed_content() {
|
574 | let mut buffer = Vec::new();
|
575 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
576 |
|
577 | let start = BytesStart::new("paired" )
|
578 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
579 | let end = start.to_end();
|
580 | let text = BytesText::new("text" );
|
581 | let inner = BytesStart::new("inner" );
|
582 |
|
583 | writer
|
584 | .write_event(Event::Start(start.clone()))
|
585 | .expect("write start tag failed" );
|
586 | writer
|
587 | .write_event(Event::Text(text))
|
588 | .expect("write text failed" );
|
589 | writer
|
590 | .write_event(Event::Empty(inner))
|
591 | .expect("write inner tag failed" );
|
592 | writer
|
593 | .write_event(Event::End(end))
|
594 | .expect("write end tag failed" );
|
595 |
|
596 | assert_eq!(
|
597 | std::str::from_utf8(&buffer).unwrap(),
|
598 | r#"<paired attr1="value1" attr2="value2">text<inner/>
|
599 | </paired>"#
|
600 | );
|
601 | }
|
602 |
|
603 | #[test ]
|
604 | fn nested() {
|
605 | let mut buffer = Vec::new();
|
606 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
607 |
|
608 | let start = BytesStart::new("paired" )
|
609 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
610 | let end = start.to_end();
|
611 | let inner = BytesStart::new("inner" );
|
612 |
|
613 | writer
|
614 | .write_event(Event::Start(start.clone()))
|
615 | .expect("write start 1 tag failed" );
|
616 | writer
|
617 | .write_event(Event::Start(start.clone()))
|
618 | .expect("write start 2 tag failed" );
|
619 | writer
|
620 | .write_event(Event::Empty(inner))
|
621 | .expect("write inner tag failed" );
|
622 | writer
|
623 | .write_event(Event::End(end.clone()))
|
624 | .expect("write end tag 2 failed" );
|
625 | writer
|
626 | .write_event(Event::End(end))
|
627 | .expect("write end tag 1 failed" );
|
628 |
|
629 | assert_eq!(
|
630 | std::str::from_utf8(&buffer).unwrap(),
|
631 | r#"<paired attr1="value1" attr2="value2">
|
632 | <paired attr1="value1" attr2="value2">
|
633 | <inner/>
|
634 | </paired>
|
635 | </paired>"#
|
636 | );
|
637 | }
|
638 |
|
639 | #[cfg (feature = "serialize" )]
|
640 | #[test ]
|
641 | fn serializable() {
|
642 | #[derive (Serialize)]
|
643 | struct Foo {
|
644 | #[serde(rename = "@attribute" )]
|
645 | attribute: &'static str,
|
646 |
|
647 | element: Bar,
|
648 | list: Vec<&'static str>,
|
649 |
|
650 | #[serde(rename = "$text" )]
|
651 | text: &'static str,
|
652 |
|
653 | val: String,
|
654 | }
|
655 |
|
656 | #[derive (Serialize)]
|
657 | struct Bar {
|
658 | baz: usize,
|
659 | bat: usize,
|
660 | }
|
661 |
|
662 | let mut buffer = Vec::new();
|
663 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
664 |
|
665 | let content = Foo {
|
666 | attribute: "attribute" ,
|
667 | element: Bar { baz: 42, bat: 43 },
|
668 | list: vec!["first element" , "second element" ],
|
669 | text: "text" ,
|
670 | val: "foo" .to_owned(),
|
671 | };
|
672 |
|
673 | let start = BytesStart::new("paired" )
|
674 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
675 | let end = start.to_end();
|
676 |
|
677 | writer
|
678 | .write_event(Event::Start(start.clone()))
|
679 | .expect("write start tag failed" );
|
680 | writer
|
681 | .write_serializable("foo_element" , &content)
|
682 | .expect("write serializable inner contents failed" );
|
683 | writer
|
684 | .write_event(Event::End(end))
|
685 | .expect("write end tag failed" );
|
686 |
|
687 | assert_eq!(
|
688 | std::str::from_utf8(&buffer).unwrap(),
|
689 | r#"<paired attr1="value1" attr2="value2">
|
690 | <foo_element attribute="attribute">
|
691 | <element>
|
692 | <baz>42</baz>
|
693 | <bat>43</bat>
|
694 | </element>
|
695 | <list>first element</list>
|
696 | <list>second element</list>
|
697 | text
|
698 | <val>foo</val>
|
699 | </foo_element>
|
700 | </paired>"#
|
701 | );
|
702 | }
|
703 |
|
704 | #[test ]
|
705 | fn element_writer_empty() {
|
706 | let mut buffer = Vec::new();
|
707 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
708 |
|
709 | writer
|
710 | .create_element("empty" )
|
711 | .with_attribute(("attr1" , "value1" ))
|
712 | .with_attribute(("attr2" , "value2" ))
|
713 | .write_empty()
|
714 | .expect("failure" );
|
715 |
|
716 | assert_eq!(
|
717 | std::str::from_utf8(&buffer).unwrap(),
|
718 | r#"<empty attr1="value1" attr2="value2"/>"#
|
719 | );
|
720 | }
|
721 |
|
722 | #[test ]
|
723 | fn element_writer_text() {
|
724 | let mut buffer = Vec::new();
|
725 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
726 |
|
727 | writer
|
728 | .create_element("paired" )
|
729 | .with_attribute(("attr1" , "value1" ))
|
730 | .with_attribute(("attr2" , "value2" ))
|
731 | .write_text_content(BytesText::new("text" ))
|
732 | .expect("failure" );
|
733 |
|
734 | assert_eq!(
|
735 | std::str::from_utf8(&buffer).unwrap(),
|
736 | r#"<paired attr1="value1" attr2="value2">text</paired>"#
|
737 | );
|
738 | }
|
739 |
|
740 | #[test ]
|
741 | fn element_writer_nested() {
|
742 | let mut buffer = Vec::new();
|
743 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
744 |
|
745 | writer
|
746 | .create_element("outer" )
|
747 | .with_attribute(("attr1" , "value1" ))
|
748 | .with_attribute(("attr2" , "value2" ))
|
749 | .write_inner_content(|writer| {
|
750 | let fruits = ["apple" , "orange" , "banana" ];
|
751 | for (quant, item) in fruits.iter().enumerate() {
|
752 | writer
|
753 | .create_element("fruit" )
|
754 | .with_attribute(("quantity" , quant.to_string().as_str()))
|
755 | .write_text_content(BytesText::new(item))?;
|
756 | }
|
757 | writer
|
758 | .create_element("inner" )
|
759 | .write_inner_content(|writer| {
|
760 | writer.create_element("empty" ).write_empty()?;
|
761 | Ok(())
|
762 | })?;
|
763 |
|
764 | Ok(())
|
765 | })
|
766 | .expect("failure" );
|
767 |
|
768 | assert_eq!(
|
769 | std::str::from_utf8(&buffer).unwrap(),
|
770 | r#"<outer attr1="value1" attr2="value2">
|
771 | <fruit quantity="0">apple</fruit>
|
772 | <fruit quantity="1">orange</fruit>
|
773 | <fruit quantity="2">banana</fruit>
|
774 | <inner>
|
775 | <empty/>
|
776 | </inner>
|
777 | </outer>"#
|
778 | );
|
779 | }
|
780 | }
|
781 | |