1 /*!
2 A simple, streaming, partially-validating XML writer that writes XML data into an internal buffer.
3 
4 ## Features
5 
6 - A simple, bare-minimum, panic-based API.
7 - Non-allocating API. All methods are accepting either `fmt::Display` or `fmt::Arguments`.
8 - Nodes auto-closing.
9 
10 ## Example
11 
12 ```rust
13 use xmlwriter::*;
14 
15 let opt = Options {
16     use_single_quote: true,
17     ..Options::default()
18 };
19 
20 let mut w = XmlWriter::new(opt);
21 w.start_element("svg");
22 w.write_attribute("xmlns", "http://www.w3.org/2000/svg");
23 w.write_attribute_fmt("viewBox", format_args!("{} {} {} {}", 0, 0, 128, 128));
24 w.start_element("text");
25 // We can write any object that implements `fmt::Display`.
26 w.write_attribute("x", &10);
27 w.write_attribute("y", &20);
28 w.write_text_fmt(format_args!("length is {}", 5));
29 
30 assert_eq!(w.end_document(),
31 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'>
32     <text x='10' y='20'>
33         length is 5
34     </text>
35 </svg>
36 ");
37 ```
38 */
39 
40 #![doc(html_root_url = "https://docs.rs/xmlwriter/0.1.0")]
41 
42 #![forbid(unsafe_code)]
43 #![warn(missing_docs)]
44 #![warn(missing_copy_implementations)]
45 
46 
47 use std::fmt::{self, Display};
48 use std::io::Write;
49 use std::ops::Range;
50 
51 
52 /// An XML node indention.
53 #[derive(Clone, Copy, PartialEq, Debug)]
54 pub enum Indent {
55     /// Disable indention and new lines.
56     None,
57     /// Indent with spaces. Preferred range is 0..4.
58     Spaces(u8),
59     /// Indent with tabs.
60     Tabs,
61 }
62 
63 /// An XML writing options.
64 #[derive(Clone, Copy, Debug)]
65 pub struct Options {
66     /// Use single quote marks instead of double quote.
67     ///
68     /// # Examples
69     ///
70     /// Before:
71     ///
72     /// ```text
73     /// <rect fill="red"/>
74     /// ```
75     ///
76     /// After:
77     ///
78     /// ```text
79     /// <rect fill='red'/>
80     /// ```
81     ///
82     /// Default: disabled
83     pub use_single_quote: bool,
84 
85     /// Set XML nodes indention.
86     ///
87     /// # Examples
88     ///
89     /// `Indent::None`
90     /// Before:
91     ///
92     /// ```text
93     /// <svg>
94     ///     <rect fill="red"/>
95     /// </svg>
96     /// ```
97     ///
98     /// After:
99     ///
100     /// ```text
101     /// <svg><rect fill="red"/></svg>
102     /// ```
103     ///
104     /// Default: 4 spaces
105     pub indent: Indent,
106 
107     /// Set XML attributes indention.
108     ///
109     /// # Examples
110     ///
111     /// `Indent::Spaces(2)`
112     ///
113     /// Before:
114     ///
115     /// ```text
116     /// <svg>
117     ///     <rect fill="red" stroke="black"/>
118     /// </svg>
119     /// ```
120     ///
121     /// After:
122     ///
123     /// ```text
124     /// <svg>
125     ///     <rect
126     ///       fill="red"
127     ///       stroke="black"/>
128     /// </svg>
129     /// ```
130     ///
131     /// Default: `None`
132     pub attributes_indent: Indent,
133 }
134 
135 impl Default for Options {
136     #[inline]
default() -> Self137     fn default() -> Self {
138         Options {
139             use_single_quote: false,
140             indent: Indent::Spaces(4),
141             attributes_indent: Indent::None,
142         }
143     }
144 }
145 
146 
147 #[derive(Clone, Copy, PartialEq, Debug)]
148 enum State {
149     Empty,
150     Document,
151     Attributes,
152 }
153 
154 struct DepthData {
155     range: Range<usize>,
156     has_children: bool,
157 }
158 
159 
160 /// An XML writer.
161 pub struct XmlWriter {
162     buf: Vec<u8>,
163     state: State,
164     preserve_whitespaces: bool,
165     depth_stack: Vec<DepthData>,
166     opt: Options,
167 }
168 
169 impl XmlWriter {
170     #[inline]
from_vec(buf: Vec<u8>, opt: Options) -> Self171     fn from_vec(buf: Vec<u8>, opt: Options) -> Self {
172         XmlWriter {
173             buf,
174             state: State::Empty,
175             preserve_whitespaces: false,
176             depth_stack: Vec::new(),
177             opt,
178         }
179     }
180 
181     /// Creates a new `XmlWriter`.
182     #[inline]
new(opt: Options) -> Self183     pub fn new(opt: Options) -> Self {
184         Self::from_vec(Vec::new(), opt)
185     }
186 
187     /// Creates a new `XmlWriter` with a specified capacity.
188     #[inline]
with_capacity(capacity: usize, opt: Options) -> Self189     pub fn with_capacity(capacity: usize, opt: Options) -> Self {
190         Self::from_vec(Vec::with_capacity(capacity), opt)
191     }
192 
193     /// Writes an XML declaration.
194     ///
195     /// `<?xml version="1.0" encoding="UTF-8" standalone="no"?>`
196     ///
197     /// # Panics
198     ///
199     /// - When called twice.
200     #[inline(never)]
write_declaration(&mut self)201     pub fn write_declaration(&mut self) {
202         if self.state != State::Empty {
203             panic!("declaration was already written");
204         }
205 
206         // Pretend that we are writing an element.
207         self.state = State::Attributes;
208 
209         // <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
210         self.push_str("<?xml");
211         self.write_attribute("version", "1.0");
212         self.write_attribute("encoding", "UTF-8");
213         self.write_attribute("standalone", "no");
214         self.push_str("?>");
215 
216         self.state = State::Document;
217     }
218 
219     /// Writes a comment string.
write_comment(&mut self, text: &str)220     pub fn write_comment(&mut self, text: &str) {
221         self.write_comment_fmt(format_args!("{}", text));
222     }
223 
224     /// Writes a formatted comment.
225     #[inline(never)]
write_comment_fmt(&mut self, fmt: fmt::Arguments)226     pub fn write_comment_fmt(&mut self, fmt: fmt::Arguments) {
227         if self.state == State::Attributes {
228             self.write_open_element();
229         }
230 
231         if self.state != State::Empty {
232             self.write_new_line();
233         }
234 
235         self.write_node_indent();
236 
237         // <!--text-->
238         self.push_str("<!--");
239         self.buf.write_fmt(fmt).unwrap(); // TODO: check content
240         self.push_str("-->");
241 
242         if self.state == State::Attributes {
243             self.depth_stack.push(DepthData {
244                 range: 0..0,
245                 has_children: false,
246             });
247         }
248 
249         self.state = State::Document;
250     }
251 
252     /// Starts writing a new element.
253     ///
254     /// This method writes only the `<tag-name` part.
255     #[inline(never)]
start_element(&mut self, name: &str)256     pub fn start_element(&mut self, name: &str) {
257         if self.state == State::Attributes {
258             self.write_open_element();
259         }
260 
261         if self.state != State::Empty {
262             self.write_new_line();
263         }
264 
265         if !self.preserve_whitespaces {
266             self.write_node_indent();
267         }
268 
269         self.push_byte(b'<');
270         let start = self.buf.len();
271         self.push_str(name);
272 
273         self.depth_stack.push(DepthData {
274             range: start..self.buf.len(),
275             has_children: false,
276         });
277 
278         self.state = State::Attributes;
279     }
280 
281     /// Writes an attribute.
282     ///
283     /// Quotes in the value will be escaped.
284     ///
285     /// # Panics
286     ///
287     /// - When called before `start_element()`.
288     /// - When called after `close_element()`.
289     ///
290     /// # Example
291     ///
292     /// ```
293     /// use xmlwriter::*;
294     ///
295     /// let mut w = XmlWriter::new(Options::default());
296     /// w.start_element("svg");
297     /// w.write_attribute("x", "5");
298     /// w.write_attribute("y", &5);
299     /// assert_eq!(w.end_document(), "<svg x=\"5\" y=\"5\"/>\n");
300     /// ```
write_attribute<V: Display + ?Sized>(&mut self, name: &str, value: &V)301     pub fn write_attribute<V: Display + ?Sized>(&mut self, name: &str, value: &V) {
302         self.write_attribute_fmt(name, format_args!("{}", value));
303     }
304 
305     /// Writes a formatted attribute value.
306     ///
307     /// Quotes in the value will be escaped.
308     ///
309     /// # Panics
310     ///
311     /// - When called before `start_element()`.
312     /// - When called after `close_element()`.
313     ///
314     /// # Example
315     ///
316     /// ```
317     /// use xmlwriter::*;
318     ///
319     /// let mut w = XmlWriter::new(Options::default());
320     /// w.start_element("rect");
321     /// w.write_attribute_fmt("fill", format_args!("url(#{})", "gradient"));
322     /// assert_eq!(w.end_document(), "<rect fill=\"url(#gradient)\"/>\n");
323     /// ```
324     #[inline(never)]
write_attribute_fmt(&mut self, name: &str, fmt: fmt::Arguments)325     pub fn write_attribute_fmt(&mut self, name: &str, fmt: fmt::Arguments) {
326         if self.state != State::Attributes {
327             panic!("must be called after start_element()");
328         }
329 
330         self.write_attribute_prefix(name);
331         let start = self.buf.len();
332         self.buf.write_fmt(fmt).unwrap();
333         self.escape_attribute_value(start);
334         self.write_quote();
335     }
336 
337     /// Writes a raw attribute value.
338     ///
339     /// Closure provides a mutable reference to an internal buffer.
340     ///
341     /// **Warning:** this method is an escape hatch for cases when you need to write
342     /// a lot of data very fast.
343     ///
344     /// # Panics
345     ///
346     /// - When called before `start_element()`.
347     /// - When called after `close_element()`.
348     ///
349     /// # Example
350     ///
351     /// ```
352     /// use xmlwriter::*;
353     ///
354     /// let mut w = XmlWriter::new(Options::default());
355     /// w.start_element("path");
356     /// w.write_attribute_raw("d", |buf| buf.extend_from_slice(b"M 10 20 L 30 40"));
357     /// assert_eq!(w.end_document(), "<path d=\"M 10 20 L 30 40\"/>\n");
358     /// ```
359     #[inline(never)]
write_attribute_raw<F>(&mut self, name: &str, f: F) where F: FnOnce(&mut Vec<u8>)360     pub fn write_attribute_raw<F>(&mut self, name: &str, f: F)
361         where F: FnOnce(&mut Vec<u8>)
362     {
363         if self.state != State::Attributes {
364             panic!("must be called after start_element()");
365         }
366 
367         self.write_attribute_prefix(name);
368         let start = self.buf.len();
369         f(&mut self.buf);
370         self.escape_attribute_value(start);
371         self.write_quote();
372     }
373 
374     #[inline(never)]
write_attribute_prefix(&mut self, name: &str)375     fn write_attribute_prefix(&mut self, name: &str) {
376         if self.opt.attributes_indent == Indent::None {
377             self.push_byte(b' ');
378         } else {
379             self.push_byte(b'\n');
380 
381             let depth = self.depth_stack.len();
382             if depth > 0 {
383                 self.write_indent(depth - 1, self.opt.indent);
384             }
385 
386             self.write_indent(1, self.opt.attributes_indent);
387         }
388 
389         self.push_str(name);
390         self.push_byte(b'=');
391         self.write_quote();
392     }
393 
394     /// Escapes the attribute value string.
395     ///
396     /// - " -> &quot;
397     /// - ' -> &apos;
398     #[inline(never)]
escape_attribute_value(&mut self, mut start: usize)399     fn escape_attribute_value(&mut self, mut start: usize) {
400         let quote = if self.opt.use_single_quote { b'\'' } else { b'"' };
401         while let Some(idx) = self.buf[start..].iter().position(|c| *c == quote) {
402             let i = start + idx;
403             let s = if self.opt.use_single_quote { b"&apos;" } else { b"&quot;" };
404             self.buf.splice(i..i+1, s.iter().cloned());
405             start = i + 6;
406         }
407     }
408 
409     /// Sets the preserve whitespaces flag.
410     ///
411     /// - If set, text nodes will be written as is.
412     /// - If not set, text nodes will be indented.
413     ///
414     /// Can be set at any moment.
415     ///
416     /// # Example
417     ///
418     /// ```
419     /// use xmlwriter::*;
420     ///
421     /// let mut w = XmlWriter::new(Options::default());
422     /// w.start_element("html");
423     /// w.start_element("p");
424     /// w.write_text("text");
425     /// w.end_element();
426     /// w.start_element("p");
427     /// w.set_preserve_whitespaces(true);
428     /// w.write_text("text");
429     /// w.end_element();
430     /// w.set_preserve_whitespaces(false);
431     /// assert_eq!(w.end_document(),
432     /// "<html>
433     ///     <p>
434     ///         text
435     ///     </p>
436     ///     <p>text</p>
437     /// </html>
438     /// ");
439     /// ```
set_preserve_whitespaces(&mut self, preserve: bool)440     pub fn set_preserve_whitespaces(&mut self, preserve: bool) {
441         self.preserve_whitespaces = preserve;
442     }
443 
444     /// Writes a text node.
445     ///
446     /// See `write_text_fmt()` for details.
write_text(&mut self, text: &str)447     pub fn write_text(&mut self, text: &str) {
448         self.write_text_fmt(format_args!("{}", text));
449     }
450 
451     /// Writes a formatted text node.
452     ///
453     /// `<` will be escaped.
454     ///
455     /// # Panics
456     ///
457     /// - When called not after `start_element()`.
458     #[inline(never)]
write_text_fmt(&mut self, fmt: fmt::Arguments)459     pub fn write_text_fmt(&mut self, fmt: fmt::Arguments) {
460         if self.state == State::Empty || self.depth_stack.is_empty() {
461             panic!("must be called after start_element()");
462         }
463 
464         if self.state == State::Attributes {
465             self.write_open_element();
466         }
467 
468         if self.state != State::Empty {
469             self.write_new_line();
470         }
471 
472         self.write_node_indent();
473 
474         let start = self.buf.len();
475         self.buf.write_fmt(fmt).unwrap();
476         self.escape_text(start);
477 
478         if self.state == State::Attributes {
479             self.depth_stack.push(DepthData {
480                 range: 0..0,
481                 has_children: false,
482             });
483         }
484 
485         self.state = State::Document;
486     }
487 
escape_text(&mut self, mut start: usize)488     fn escape_text(&mut self, mut start: usize) {
489         while let Some(idx) = self.buf[start..].iter().position(|c| *c == b'<') {
490             let i = start + idx;
491             self.buf.splice(i..i+1, b"&lt;".iter().cloned());
492             start = i + 4;
493         }
494     }
495 
496     /// Closes an open element.
497     #[inline(never)]
end_element(&mut self)498     pub fn end_element(&mut self) {
499         if let Some(depth) = self.depth_stack.pop() {
500             if depth.has_children {
501                 if !self.preserve_whitespaces {
502                     self.write_new_line();
503                     self.write_node_indent();
504                 }
505 
506                 self.push_str("</");
507 
508                 for i in depth.range {
509                     self.push_byte(self.buf[i]);
510                 }
511 
512                 self.push_byte(b'>');
513             } else {
514                 self.push_str("/>");
515             }
516         }
517 
518         self.state = State::Document;
519     }
520 
521     /// Closes all open elements and returns an internal XML buffer.
522     ///
523     /// # Example
524     ///
525     /// ```
526     /// use xmlwriter::*;
527     ///
528     /// let mut w = XmlWriter::new(Options::default());
529     /// w.start_element("svg");
530     /// w.start_element("g");
531     /// w.start_element("rect");
532     /// assert_eq!(w.end_document(),
533     /// "<svg>
534     ///     <g>
535     ///         <rect/>
536     ///     </g>
537     /// </svg>
538     /// ");
539     /// ```
end_document(mut self) -> String540     pub fn end_document(mut self) -> String {
541         while !self.depth_stack.is_empty() {
542             self.end_element();
543         }
544 
545         self.write_new_line();
546 
547         // The only way it can fail is if an invalid data
548         // was written via `write_attribute_raw()`.
549         String::from_utf8(self.buf).unwrap()
550     }
551 
552     #[inline]
push_byte(&mut self, c: u8)553     fn push_byte(&mut self, c: u8) {
554         self.buf.push(c);
555     }
556 
557     #[inline]
push_str(&mut self, text: &str)558     fn push_str(&mut self, text: &str) {
559         self.buf.extend_from_slice(text.as_bytes());
560     }
561 
562     #[inline]
get_quote_char(&self) -> u8563     fn get_quote_char(&self) -> u8 {
564         if self.opt.use_single_quote { b'\'' } else { b'"' }
565     }
566 
567     #[inline]
write_quote(&mut self)568     fn write_quote(&mut self) {
569         self.push_byte(self.get_quote_char());
570     }
571 
write_open_element(&mut self)572     fn write_open_element(&mut self) {
573         if let Some(depth) = self.depth_stack.last_mut() {
574             depth.has_children = true;
575             self.push_byte(b'>');
576 
577             self.state = State::Document;
578         }
579     }
580 
write_node_indent(&mut self)581     fn write_node_indent(&mut self) {
582         self.write_indent(self.depth_stack.len(), self.opt.indent);
583     }
584 
write_indent(&mut self, depth: usize, indent: Indent)585     fn write_indent(&mut self, depth: usize, indent: Indent) {
586         if indent == Indent::None || self.preserve_whitespaces {
587             return;
588         }
589 
590         for _ in 0..depth {
591             match indent {
592                 Indent::None => {}
593                 Indent::Spaces(n) => {
594                     for _ in 0..n {
595                         self.push_byte(b' ');
596                     }
597                 }
598                 Indent::Tabs => self.push_byte(b'\t'),
599             }
600         }
601     }
602 
write_new_line(&mut self)603     fn write_new_line(&mut self) {
604         if self.opt.indent != Indent::None && !self.preserve_whitespaces {
605             self.push_byte(b'\n');
606         }
607     }
608 }
609