1 | //! Contains high-level interface for an events-based XML emitter.
|
2 |
|
3 | use std::io::Write;
|
4 | use std::result::Result as StdResult;
|
5 |
|
6 | use crate::encoding::UTF8_BOM;
|
7 | use crate::errors::{Error, Result};
|
8 | use crate::events::{attributes::Attribute, BytesCData, BytesStart, BytesText, Event};
|
9 |
|
10 | #[cfg (feature = "async-tokio" )]
|
11 | mod async_tokio;
|
12 |
|
13 | /// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] or [`tokio::io::AsyncWrite`] implementor.
|
14 | #[cfg (feature = "serialize" )]
|
15 | use {crate::de::DeError, serde::Serialize};
|
16 |
|
17 | /// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] implementor.
|
18 | ///
|
19 | /// # Examples
|
20 | ///
|
21 | /// ```
|
22 | /// # use pretty_assertions::assert_eq;
|
23 | /// use quick_xml::events::{Event, BytesEnd, BytesStart};
|
24 | /// use quick_xml::reader::Reader;
|
25 | /// use quick_xml::writer::Writer;
|
26 | /// use std::io::Cursor;
|
27 | ///
|
28 | /// let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"# ;
|
29 | /// let mut reader = Reader::from_str(xml);
|
30 | /// reader.trim_text(true);
|
31 | /// let mut writer = Writer::new(Cursor::new(Vec::new()));
|
32 | /// loop {
|
33 | /// match reader.read_event() {
|
34 | /// Ok(Event::Start(e)) if e.name().as_ref() == b"this_tag" => {
|
35 | ///
|
36 | /// // crates a new element ... alternatively we could reuse `e` by calling
|
37 | /// // `e.into_owned()`
|
38 | /// let mut elem = BytesStart::new("my_elem" );
|
39 | ///
|
40 | /// // collect existing attributes
|
41 | /// elem.extend_attributes(e.attributes().map(|attr| attr.unwrap()));
|
42 | ///
|
43 | /// // copy existing attributes, adds a new my-key="some value" attribute
|
44 | /// elem.push_attribute(("my-key" , "some value" ));
|
45 | ///
|
46 | /// // writes the event to the writer
|
47 | /// assert!(writer.write_event(Event::Start(elem)).is_ok());
|
48 | /// },
|
49 | /// Ok(Event::End(e)) if e.name().as_ref() == b"this_tag" => {
|
50 | /// assert!(writer.write_event(Event::End(BytesEnd::new("my_elem" ))).is_ok());
|
51 | /// },
|
52 | /// Ok(Event::Eof) => break,
|
53 | /// // we can either move or borrow the event to write, depending on your use-case
|
54 | /// Ok(e) => assert!(writer.write_event(e).is_ok()),
|
55 | /// Err(e) => panic!("Error at position {}: {:?}" , reader.buffer_position(), e),
|
56 | /// }
|
57 | /// }
|
58 | ///
|
59 | /// let result = writer.into_inner().into_inner();
|
60 | /// let expected = r#"<my_elem k1="v1" k2="v2" my-key="some value"><child>text</child></my_elem>"# ;
|
61 | /// assert_eq!(result, expected.as_bytes());
|
62 | /// ```
|
63 | #[derive (Clone)]
|
64 | pub struct Writer<W> {
|
65 | /// underlying writer
|
66 | writer: W,
|
67 | indent: Option<Indentation>,
|
68 | }
|
69 |
|
70 | impl<W> Writer<W> {
|
71 | /// Creates a `Writer` from a generic writer.
|
72 | pub fn new(inner: W) -> Writer<W> {
|
73 | Writer {
|
74 | writer: inner,
|
75 | indent: None,
|
76 | }
|
77 | }
|
78 |
|
79 | /// Creates a `Writer` with configured indents from a generic writer.
|
80 | pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
|
81 | Writer {
|
82 | writer: inner,
|
83 | indent: Some(Indentation::new(indent_char, indent_size)),
|
84 | }
|
85 | }
|
86 |
|
87 | /// Consumes this `Writer`, returning the underlying writer.
|
88 | pub fn into_inner(self) -> W {
|
89 | self.writer
|
90 | }
|
91 |
|
92 | /// Get a mutable reference to the underlying writer.
|
93 | pub fn get_mut(&mut self) -> &mut W {
|
94 | &mut self.writer
|
95 | }
|
96 |
|
97 | /// Get a reference to the underlying writer.
|
98 | pub fn get_ref(&self) -> &W {
|
99 | &self.writer
|
100 | }
|
101 |
|
102 | /// Provides a simple, high-level API for writing XML elements.
|
103 | ///
|
104 | /// Returns an [`ElementWriter`] that simplifies setting attributes and writing
|
105 | /// content inside the element.
|
106 | ///
|
107 | /// # Example
|
108 | ///
|
109 | /// ```
|
110 | /// # use quick_xml::Result;
|
111 | /// # fn main() -> Result<()> {
|
112 | /// use quick_xml::events::{BytesStart, BytesText, Event};
|
113 | /// use quick_xml::writer::Writer;
|
114 | /// use quick_xml::Error;
|
115 | /// use std::io::Cursor;
|
116 | ///
|
117 | /// let mut writer = Writer::new(Cursor::new(Vec::new()));
|
118 | ///
|
119 | /// // writes <tag attr1="value1"/>
|
120 | /// writer.create_element("tag" )
|
121 | /// .with_attribute(("attr1" , "value1" )) // chain `with_attribute()` calls to add many attributes
|
122 | /// .write_empty()?;
|
123 | ///
|
124 | /// // writes <tag attr1="value1" attr2="value2">with some text inside</tag>
|
125 | /// writer.create_element("tag" )
|
126 | /// .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter()) // or add attributes from an iterator
|
127 | /// .write_text_content(BytesText::new("with some text inside" ))?;
|
128 | ///
|
129 | /// // writes <tag><fruit quantity="0">apple</fruit><fruit quantity="1">orange</fruit></tag>
|
130 | /// writer.create_element("tag" )
|
131 | /// // We need to provide error type, because it is not named somewhere explicitly
|
132 | /// .write_inner_content::<_, Error>(|writer| {
|
133 | /// let fruits = ["apple" , "orange" ];
|
134 | /// for (quant, item) in fruits.iter().enumerate() {
|
135 | /// writer
|
136 | /// .create_element("fruit" )
|
137 | /// .with_attribute(("quantity" , quant.to_string().as_str()))
|
138 | /// .write_text_content(BytesText::new(item))?;
|
139 | /// }
|
140 | /// Ok(())
|
141 | /// })?;
|
142 | /// # Ok(())
|
143 | /// # }
|
144 | /// ```
|
145 | #[must_use ]
|
146 | pub fn create_element<'a, N>(&'a mut self, name: &'a N) -> ElementWriter<W>
|
147 | where
|
148 | N: 'a + AsRef<str> + ?Sized,
|
149 | {
|
150 | ElementWriter {
|
151 | writer: self,
|
152 | start_tag: BytesStart::new(name.as_ref()),
|
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) -> Result<()> {
|
189 | self.write(UTF8_BOM)
|
190 | }
|
191 |
|
192 | /// Writes the given event to the underlying writer.
|
193 | pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
|
194 | let mut next_should_line_break = true;
|
195 | let result = match *event.as_ref() {
|
196 | Event::Start(ref 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(ref 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(ref e) => self.write_wrapped(b"<" , e, b"/>" ),
|
210 | Event::Text(ref e) => {
|
211 | next_should_line_break = false;
|
212 | self.write(e)
|
213 | }
|
214 | Event::Comment(ref e) => self.write_wrapped(b"<!--" , e, b"-->" ),
|
215 | Event::CData(ref e) => {
|
216 | next_should_line_break = false;
|
217 | self.write(b"<![CDATA[" )?;
|
218 | self.write(e)?;
|
219 | self.write(b"]]>" )
|
220 | }
|
221 | Event::Decl(ref e) => self.write_wrapped(b"<?" , e, b"?>" ),
|
222 | Event::PI(ref e) => self.write_wrapped(b"<?" , e, b"?>" ),
|
223 | Event::DocType(ref 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]) -> 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]) -> 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) -> 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::DeError;
|
282 | /// # fn main() -> Result<(), DeError> {
|
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 | ) -> std::result::Result<(), DeError> {
|
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 | /// A struct to write an element. Contains methods to add attributes and inner
|
339 | /// elements to the element
|
340 | pub struct ElementWriter<'a, W> {
|
341 | writer: &'a mut Writer<W>,
|
342 | start_tag: BytesStart<'a>,
|
343 | }
|
344 |
|
345 | impl<'a, W> ElementWriter<'a, W> {
|
346 | /// Adds an attribute to this element.
|
347 | pub fn with_attribute<'b, I>(mut self, attr: I) -> Self
|
348 | where
|
349 | I: Into<Attribute<'b>>,
|
350 | {
|
351 | self.start_tag.push_attribute(attr);
|
352 | self
|
353 | }
|
354 |
|
355 | /// Add additional attributes to this element using an iterator.
|
356 | ///
|
357 | /// The yielded items must be convertible to [`Attribute`] using `Into`.
|
358 | pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
|
359 | where
|
360 | I: IntoIterator,
|
361 | I::Item: Into<Attribute<'b>>,
|
362 | {
|
363 | self.start_tag.extend_attributes(attributes);
|
364 | self
|
365 | }
|
366 | }
|
367 |
|
368 | impl<'a, W: Write> ElementWriter<'a, W> {
|
369 | /// Write some text inside the current element.
|
370 | pub fn write_text_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
|
371 | self.writer
|
372 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
373 | self.writer.write_event(Event::Text(text))?;
|
374 | self.writer
|
375 | .write_event(Event::End(self.start_tag.to_end()))?;
|
376 | Ok(self.writer)
|
377 | }
|
378 |
|
379 | /// Write a CData event `<![CDATA[...]]>` inside the current element.
|
380 | pub fn write_cdata_content(self, text: BytesCData) -> Result<&'a mut Writer<W>> {
|
381 | self.writer
|
382 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
383 | self.writer.write_event(Event::CData(text))?;
|
384 | self.writer
|
385 | .write_event(Event::End(self.start_tag.to_end()))?;
|
386 | Ok(self.writer)
|
387 | }
|
388 |
|
389 | /// Write a processing instruction `<?...?>` inside the current element.
|
390 | pub fn write_pi_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
|
391 | self.writer
|
392 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
393 | self.writer.write_event(Event::PI(text))?;
|
394 | self.writer
|
395 | .write_event(Event::End(self.start_tag.to_end()))?;
|
396 | Ok(self.writer)
|
397 | }
|
398 |
|
399 | /// Write an empty (self-closing) tag.
|
400 | pub fn write_empty(self) -> Result<&'a mut Writer<W>> {
|
401 | self.writer.write_event(Event::Empty(self.start_tag))?;
|
402 | Ok(self.writer)
|
403 | }
|
404 |
|
405 | /// Create a new scope for writing XML inside the current element.
|
406 | pub fn write_inner_content<F, E>(self, closure: F) -> StdResult<&'a mut Writer<W>, E>
|
407 | where
|
408 | F: FnOnce(&mut Writer<W>) -> StdResult<(), E>,
|
409 | E: From<Error>,
|
410 | {
|
411 | self.writer
|
412 | .write_event(Event::Start(self.start_tag.borrow()))?;
|
413 | closure(self.writer)?;
|
414 | self.writer
|
415 | .write_event(Event::End(self.start_tag.to_end()))?;
|
416 | Ok(self.writer)
|
417 | }
|
418 | }
|
419 | #[cfg (feature = "serialize" )]
|
420 | struct ToFmtWrite<T>(pub T);
|
421 |
|
422 | #[cfg (feature = "serialize" )]
|
423 | impl<T> std::fmt::Write for ToFmtWrite<T>
|
424 | where
|
425 | T: std::io::Write,
|
426 | {
|
427 | fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
428 | self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
|
429 | }
|
430 | }
|
431 |
|
432 | #[derive (Clone)]
|
433 | pub(crate) struct Indentation {
|
434 | /// todo: this is an awkward fit as it has no impact on indentation logic, but it is
|
435 | /// only applicable when an indentation exists. Potentially refactor later
|
436 | should_line_break: bool,
|
437 | /// The character code to be used for indentations (e.g. ` ` or `\t`)
|
438 | indent_char: u8,
|
439 | /// How many instances of the indent character ought to be used for each level of indentation
|
440 | indent_size: usize,
|
441 | /// Used as a cache for the bytes used for indentation
|
442 | indents: Vec<u8>,
|
443 | /// The current amount of indentation
|
444 | current_indent_len: usize,
|
445 | }
|
446 |
|
447 | impl Indentation {
|
448 | pub fn new(indent_char: u8, indent_size: usize) -> Self {
|
449 | Self {
|
450 | should_line_break: false,
|
451 | indent_char,
|
452 | indent_size,
|
453 | indents: vec![indent_char; 128],
|
454 | current_indent_len: 0, // invariant - needs to remain less than indents.len()
|
455 | }
|
456 | }
|
457 |
|
458 | /// Increase indentation by one level
|
459 | pub fn grow(&mut self) {
|
460 | self.current_indent_len += self.indent_size;
|
461 | if self.current_indent_len > self.indents.len() {
|
462 | self.indents
|
463 | .resize(self.current_indent_len, self.indent_char);
|
464 | }
|
465 | }
|
466 |
|
467 | /// Decrease indentation by one level. Do nothing, if level already zero
|
468 | pub fn shrink(&mut self) {
|
469 | self.current_indent_len = self.current_indent_len.saturating_sub(self.indent_size);
|
470 | }
|
471 |
|
472 | /// Returns indent string for current level
|
473 | pub fn current(&self) -> &[u8] {
|
474 | &self.indents[..self.current_indent_len]
|
475 | }
|
476 | }
|
477 |
|
478 | #[cfg (test)]
|
479 | mod indentation {
|
480 | use super::*;
|
481 | use crate::events::*;
|
482 | use pretty_assertions::assert_eq;
|
483 |
|
484 | #[test ]
|
485 | fn self_closed() {
|
486 | let mut buffer = Vec::new();
|
487 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
488 |
|
489 | let tag = BytesStart::new("self-closed" )
|
490 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
491 | writer
|
492 | .write_event(Event::Empty(tag))
|
493 | .expect("write tag failed" );
|
494 |
|
495 | assert_eq!(
|
496 | std::str::from_utf8(&buffer).unwrap(),
|
497 | r#"<self-closed attr1="value1" attr2="value2"/>"#
|
498 | );
|
499 | }
|
500 |
|
501 | #[test ]
|
502 | fn empty_paired() {
|
503 | let mut buffer = Vec::new();
|
504 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
505 |
|
506 | let start = BytesStart::new("paired" )
|
507 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
508 | let end = start.to_end();
|
509 | writer
|
510 | .write_event(Event::Start(start.clone()))
|
511 | .expect("write start tag failed" );
|
512 | writer
|
513 | .write_event(Event::End(end))
|
514 | .expect("write end tag failed" );
|
515 |
|
516 | assert_eq!(
|
517 | std::str::from_utf8(&buffer).unwrap(),
|
518 | r#"<paired attr1="value1" attr2="value2">
|
519 | </paired>"#
|
520 | );
|
521 | }
|
522 |
|
523 | #[test ]
|
524 | fn paired_with_inner() {
|
525 | let mut buffer = Vec::new();
|
526 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
527 |
|
528 | let start = BytesStart::new("paired" )
|
529 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
530 | let end = start.to_end();
|
531 | let inner = BytesStart::new("inner" );
|
532 |
|
533 | writer
|
534 | .write_event(Event::Start(start.clone()))
|
535 | .expect("write start tag failed" );
|
536 | writer
|
537 | .write_event(Event::Empty(inner))
|
538 | .expect("write inner tag failed" );
|
539 | writer
|
540 | .write_event(Event::End(end))
|
541 | .expect("write end tag failed" );
|
542 |
|
543 | assert_eq!(
|
544 | std::str::from_utf8(&buffer).unwrap(),
|
545 | r#"<paired attr1="value1" attr2="value2">
|
546 | <inner/>
|
547 | </paired>"#
|
548 | );
|
549 | }
|
550 |
|
551 | #[test ]
|
552 | fn paired_with_text() {
|
553 | let mut buffer = Vec::new();
|
554 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
555 |
|
556 | let start = BytesStart::new("paired" )
|
557 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
558 | let end = start.to_end();
|
559 | let text = BytesText::new("text" );
|
560 |
|
561 | writer
|
562 | .write_event(Event::Start(start.clone()))
|
563 | .expect("write start tag failed" );
|
564 | writer
|
565 | .write_event(Event::Text(text))
|
566 | .expect("write text failed" );
|
567 | writer
|
568 | .write_event(Event::End(end))
|
569 | .expect("write end tag failed" );
|
570 |
|
571 | assert_eq!(
|
572 | std::str::from_utf8(&buffer).unwrap(),
|
573 | r#"<paired attr1="value1" attr2="value2">text</paired>"#
|
574 | );
|
575 | }
|
576 |
|
577 | #[test ]
|
578 | fn mixed_content() {
|
579 | let mut buffer = Vec::new();
|
580 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
581 |
|
582 | let start = BytesStart::new("paired" )
|
583 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
584 | let end = start.to_end();
|
585 | let text = BytesText::new("text" );
|
586 | let inner = BytesStart::new("inner" );
|
587 |
|
588 | writer
|
589 | .write_event(Event::Start(start.clone()))
|
590 | .expect("write start tag failed" );
|
591 | writer
|
592 | .write_event(Event::Text(text))
|
593 | .expect("write text failed" );
|
594 | writer
|
595 | .write_event(Event::Empty(inner))
|
596 | .expect("write inner tag failed" );
|
597 | writer
|
598 | .write_event(Event::End(end))
|
599 | .expect("write end tag failed" );
|
600 |
|
601 | assert_eq!(
|
602 | std::str::from_utf8(&buffer).unwrap(),
|
603 | r#"<paired attr1="value1" attr2="value2">text<inner/>
|
604 | </paired>"#
|
605 | );
|
606 | }
|
607 |
|
608 | #[test ]
|
609 | fn nested() {
|
610 | let mut buffer = Vec::new();
|
611 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
612 |
|
613 | let start = BytesStart::new("paired" )
|
614 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
615 | let end = start.to_end();
|
616 | let inner = BytesStart::new("inner" );
|
617 |
|
618 | writer
|
619 | .write_event(Event::Start(start.clone()))
|
620 | .expect("write start 1 tag failed" );
|
621 | writer
|
622 | .write_event(Event::Start(start.clone()))
|
623 | .expect("write start 2 tag failed" );
|
624 | writer
|
625 | .write_event(Event::Empty(inner))
|
626 | .expect("write inner tag failed" );
|
627 | writer
|
628 | .write_event(Event::End(end.clone()))
|
629 | .expect("write end tag 2 failed" );
|
630 | writer
|
631 | .write_event(Event::End(end))
|
632 | .expect("write end tag 1 failed" );
|
633 |
|
634 | assert_eq!(
|
635 | std::str::from_utf8(&buffer).unwrap(),
|
636 | r#"<paired attr1="value1" attr2="value2">
|
637 | <paired attr1="value1" attr2="value2">
|
638 | <inner/>
|
639 | </paired>
|
640 | </paired>"#
|
641 | );
|
642 | }
|
643 |
|
644 | #[cfg (feature = "serialize" )]
|
645 | #[test ]
|
646 | fn serializable() {
|
647 | #[derive (Serialize)]
|
648 | struct Foo {
|
649 | #[serde(rename = "@attribute" )]
|
650 | attribute: &'static str,
|
651 |
|
652 | element: Bar,
|
653 | list: Vec<&'static str>,
|
654 |
|
655 | #[serde(rename = "$text" )]
|
656 | text: &'static str,
|
657 |
|
658 | val: String,
|
659 | }
|
660 |
|
661 | #[derive (Serialize)]
|
662 | struct Bar {
|
663 | baz: usize,
|
664 | bat: usize,
|
665 | }
|
666 |
|
667 | let mut buffer = Vec::new();
|
668 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
669 |
|
670 | let content = Foo {
|
671 | attribute: "attribute" ,
|
672 | element: Bar { baz: 42, bat: 43 },
|
673 | list: vec!["first element" , "second element" ],
|
674 | text: "text" ,
|
675 | val: "foo" .to_owned(),
|
676 | };
|
677 |
|
678 | let start = BytesStart::new("paired" )
|
679 | .with_attributes(vec![("attr1" , "value1" ), ("attr2" , "value2" )].into_iter());
|
680 | let end = start.to_end();
|
681 |
|
682 | writer
|
683 | .write_event(Event::Start(start.clone()))
|
684 | .expect("write start tag failed" );
|
685 | writer
|
686 | .write_serializable("foo_element" , &content)
|
687 | .expect("write serializable inner contents failed" );
|
688 | writer
|
689 | .write_event(Event::End(end))
|
690 | .expect("write end tag failed" );
|
691 |
|
692 | assert_eq!(
|
693 | std::str::from_utf8(&buffer).unwrap(),
|
694 | r#"<paired attr1="value1" attr2="value2">
|
695 | <foo_element attribute="attribute">
|
696 | <element>
|
697 | <baz>42</baz>
|
698 | <bat>43</bat>
|
699 | </element>
|
700 | <list>first element</list>
|
701 | <list>second element</list>
|
702 | text
|
703 | <val>foo</val>
|
704 | </foo_element>
|
705 | </paired>"#
|
706 | );
|
707 | }
|
708 |
|
709 | #[test ]
|
710 | fn element_writer_empty() {
|
711 | let mut buffer = Vec::new();
|
712 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
713 |
|
714 | writer
|
715 | .create_element("empty" )
|
716 | .with_attribute(("attr1" , "value1" ))
|
717 | .with_attribute(("attr2" , "value2" ))
|
718 | .write_empty()
|
719 | .expect("failure" );
|
720 |
|
721 | assert_eq!(
|
722 | std::str::from_utf8(&buffer).unwrap(),
|
723 | r#"<empty attr1="value1" attr2="value2"/>"#
|
724 | );
|
725 | }
|
726 |
|
727 | #[test ]
|
728 | fn element_writer_text() {
|
729 | let mut buffer = Vec::new();
|
730 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
731 |
|
732 | writer
|
733 | .create_element("paired" )
|
734 | .with_attribute(("attr1" , "value1" ))
|
735 | .with_attribute(("attr2" , "value2" ))
|
736 | .write_text_content(BytesText::new("text" ))
|
737 | .expect("failure" );
|
738 |
|
739 | assert_eq!(
|
740 | std::str::from_utf8(&buffer).unwrap(),
|
741 | r#"<paired attr1="value1" attr2="value2">text</paired>"#
|
742 | );
|
743 | }
|
744 |
|
745 | #[test ]
|
746 | fn element_writer_nested() {
|
747 | let mut buffer = Vec::new();
|
748 | let mut writer = Writer::new_with_indent(&mut buffer, b' ' , 4);
|
749 |
|
750 | writer
|
751 | .create_element("outer" )
|
752 | .with_attribute(("attr1" , "value1" ))
|
753 | .with_attribute(("attr2" , "value2" ))
|
754 | .write_inner_content::<_, Error>(|writer| {
|
755 | let fruits = ["apple" , "orange" , "banana" ];
|
756 | for (quant, item) in fruits.iter().enumerate() {
|
757 | writer
|
758 | .create_element("fruit" )
|
759 | .with_attribute(("quantity" , quant.to_string().as_str()))
|
760 | .write_text_content(BytesText::new(item))?;
|
761 | }
|
762 | writer
|
763 | .create_element("inner" )
|
764 | .write_inner_content(|writer| {
|
765 | writer.create_element("empty" ).write_empty().map(|_| ())
|
766 | })?;
|
767 |
|
768 | Ok(())
|
769 | })
|
770 | .expect("failure" );
|
771 |
|
772 | assert_eq!(
|
773 | std::str::from_utf8(&buffer).unwrap(),
|
774 | r#"<outer attr1="value1" attr2="value2">
|
775 | <fruit quantity="0">apple</fruit>
|
776 | <fruit quantity="1">orange</fruit>
|
777 | <fruit quantity="2">banana</fruit>
|
778 | <inner>
|
779 | <empty/>
|
780 | </inner>
|
781 | </outer>"#
|
782 | );
|
783 | }
|
784 | }
|
785 | |