1//! A minimal DOM crate built on top of xml-rs.
2//!
3//! This library exports an `Element` struct which represents a DOM tree.
4//!
5//! # Example
6//!
7//! Run with `cargo run --example articles`. Located in `examples/articles.rs`.
8//!
9//! ```rust,ignore
10//! extern crate minidom;
11//!
12//! use minidom::Element;
13//!
14//! const DATA: &'static str = r#"<articles xmlns="article">
15//! <article>
16//! <title>10 Terrible Bugs You Would NEVER Believe Happened</title>
17//! <body>
18//! Rust fixed them all. <3
19//! </body>
20//! </article>
21//! <article>
22//! <title>BREAKING NEWS: Physical Bug Jumps Out Of Programmer's Screen</title>
23//! <body>
24//! Just kidding!
25//! </body>
26//! </article>
27//! </articles>"#;
28//!
29//! const ARTICLE_NS: &'static str = "article";
30//!
31//! #[derive(Debug)]
32//! pub struct Article {
33//! title: String,
34//! body: String,
35//! }
36//!
37//! fn main() {
38//! let root: Element = DATA.parse().unwrap();
39//!
40//! let mut articles: Vec<Article> = Vec::new();
41//!
42//! for child in root.children() {
43//! if child.is("article", ARTICLE_NS) {
44//! let title = child.get_child("title", ARTICLE_NS).unwrap().text();
45//! let body = child.get_child("body", ARTICLE_NS).unwrap().text();
46//! articles.push(Article {
47//! title: title,
48//! body: body.trim().to_owned(),
49//! });
50//! }
51//! }
52//!
53//! println!("{:?}", articles);
54//! }
55//! ```
56//!
57//! # Usage
58//!
59//! To use `minidom`, add this to your `Cargo.toml`:
60//!
61//! ```toml,ignore
62//! [dependencies.minidom]
63//! git = "https://gitlab.com/lumi/minidom-rs.git"
64//! ```
65
66extern crate xml;
67
68mod error;
69
70mod attribute;
71
72use std::io::prelude::*;
73use std::io::Cursor;
74
75use std::convert::AsRef;
76
77use std::iter::Iterator;
78
79use std::slice;
80
81use std::fmt;
82
83use std::str::FromStr;
84
85use xml::reader::{XmlEvent as ReaderEvent, EventReader};
86use xml::writer::{XmlEvent as WriterEvent, EventWriter};
87use xml::name::Name;
88use xml::namespace::NS_NO_PREFIX;
89
90pub use error::Error;
91
92pub use attribute::Attribute;
93
94#[derive(Clone, PartialEq, Eq)]
95/// A struct representing a DOM Element.
96pub struct Element {
97 name: String,
98 namespace: Option<String>,
99 attributes: Vec<Attribute>,
100 children: Vec<Fork>,
101}
102
103impl fmt::Debug for Element {
104 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
105 if let Some(ref ns) = self.namespace {
106 write!(fmt, "<{{{}}}{}", ns, self.name)?;
107 }
108 else {
109 write!(fmt, "<{}", self.name)?;
110 }
111 for attr in &self.attributes {
112 write!(fmt, " {}", attr)?;
113 }
114 write!(fmt, ">")?;
115 for child in &self.children {
116 match *child {
117 Fork::Element(ref e) => {
118 write!(fmt, "{:?}", e)?;
119 },
120 Fork::Text(ref s) => {
121 write!(fmt, "{}", s)?;
122 },
123 }
124 }
125 write!(fmt, "</{}>", self.name)?;
126 Ok(())
127 }
128}
129
130impl FromStr for Element {
131 type Err = Error;
132
133 fn from_str(s: &str) -> Result<Element, Error> {
134 let mut reader = EventReader::new(Cursor::new(s));
135 Element::from_reader(&mut reader)
136 }
137}
138
139#[derive(Clone, Debug, PartialEq, Eq)]
140enum Fork {
141 Element(Element),
142 Text(String),
143}
144
145impl Element {
146 /// Constructs a new `Element` with the given `name`, `namespace` and `attributes`.
147 ///
148 /// You probably should be using `Element::builder` instead of this.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use minidom::{Element, Attribute};
154 ///
155 /// let elem = Element::new( "name".to_owned()
156 /// , Some("namespace".to_owned())
157 /// , vec![ Attribute::new("name", "value") ] );
158 ///
159 /// assert_eq!(elem.name(), "name");
160 /// assert_eq!(elem.ns(), Some("namespace"));
161 /// assert_eq!(elem.attr("name"), Some("value"));
162 /// assert_eq!(elem.attr("inexistent"), None);
163 /// ```
164 pub fn new(name: String, namespace: Option<String>, attributes: Vec<Attribute>) -> Element {
165 Element {
166 name: name,
167 namespace: namespace,
168 attributes: attributes,
169 children: Vec::new(),
170 }
171 }
172
173 /// Return a builder for an `Element` with the given `name`.
174 ///
175 /// # Examples
176 ///
177 /// ```
178 /// use minidom::Element;
179 ///
180 /// let elem = Element::builder("name")
181 /// .ns("namespace")
182 /// .attr("name", "value")
183 /// .text("inner")
184 /// .build();
185 ///
186 /// assert_eq!(elem.name(), "name");
187 /// assert_eq!(elem.ns(), Some("namespace"));
188 /// assert_eq!(elem.attr("name"), Some("value"));
189 /// assert_eq!(elem.attr("inexistent"), None);
190 /// assert_eq!(elem.text(), "inner");
191 /// ```
192 pub fn builder<S: Into<String>>(name: S) -> ElementBuilder {
193 ElementBuilder {
194 name: name.into(),
195 text: None,
196 namespace: None,
197 attributes: Vec::new(),
198 }
199 }
200
201 /// Returns a bare minimum `Element` with this name.
202 ///
203 /// # Examples
204 ///
205 /// ```
206 /// use minidom::Element;
207 ///
208 /// let bare = Element::bare("name");
209 ///
210 /// assert_eq!(bare.name(), "name");
211 /// assert_eq!(bare.ns(), None);
212 /// assert_eq!(bare.attr("name"), None);
213 /// assert_eq!(bare.text(), "");
214 /// ```
215 pub fn bare<S: Into<String>>(name: S) -> Element {
216 Element {
217 name: name.into(),
218 namespace: None,
219 attributes: Vec::new(),
220 children: Vec::new(),
221 }
222 }
223
224 /// Returns a reference to the name of this element.
225 pub fn name(&self) -> &str {
226 &self.name
227 }
228
229 /// Returns a reference to the namespace of this element, if it has one, else `None`.
230 pub fn ns(&self) -> Option<&str> {
231 self.namespace.as_ref()
232 .map(String::as_ref)
233 }
234
235 /// Returns a reference to the value of the given attribute, if it exists, else `None`.
236 pub fn attr(&self, name: &str) -> Option<&str> {
237 for attr in &self.attributes {
238 if attr.name == name {
239 return Some(&attr.value);
240 }
241 }
242 None
243 }
244
245 /// Returns whether the element has the given name and namespace.
246 ///
247 /// # Examples
248 ///
249 /// ```
250 /// use minidom::Element;
251 ///
252 /// let elem = Element::builder("name").ns("namespace").build();
253 ///
254 /// assert_eq!(elem.is("name", "namespace"), true);
255 /// assert_eq!(elem.is("name", "wrong"), false);
256 /// assert_eq!(elem.is("wrong", "namespace"), false);
257 /// assert_eq!(elem.is("wrong", "wrong"), false);
258 /// ```
259 pub fn is<N: AsRef<str>, NS: AsRef<str>>(&self, name: N, namespace: NS) -> bool {
260 let ns = self.namespace.as_ref().map(String::as_ref);
261 self.name == name.as_ref() && ns == Some(namespace.as_ref())
262 }
263
264 pub fn from_reader<R: Read>(reader: &mut EventReader<R>) -> Result<Element, Error> {
265 loop {
266 let e = reader.next()?;
267 match e {
268 ReaderEvent::StartElement { name, attributes, namespace } => {
269 let attributes = attributes.into_iter()
270 .map(|o| Attribute::new(o.name.local_name, o.value))
271 .collect();
272 let ns = if let Some(ref prefix) = name.prefix {
273 namespace.get(prefix)
274 }
275 else {
276 namespace.get(NS_NO_PREFIX)
277 }.map(|s| s.to_owned());
278 let mut root = Element::new(name.local_name, ns, attributes);
279 root.from_reader_inner(reader)?;
280 return Ok(root);
281 },
282 ReaderEvent::EndDocument => {
283 return Err(Error::EndOfDocument);
284 },
285 _ => () // TODO: may need more errors
286 }
287 }
288 }
289
290 fn from_reader_inner<R: Read>(&mut self, reader: &mut EventReader<R>) -> Result<(), Error> {
291 loop {
292 let e = reader.next()?;
293 match e {
294 ReaderEvent::StartElement { name, attributes, namespace } => {
295 let attributes = attributes.into_iter()
296 .map(|o| Attribute::new(o.name.local_name, o.value))
297 .collect();
298 let ns = if let Some(ref prefix) = name.prefix {
299 namespace.get(prefix)
300 }
301 else {
302 namespace.get(NS_NO_PREFIX)
303 }.map(|s| s.to_owned());
304 let elem = Element::new(name.local_name, ns, attributes);
305 let elem_ref = self.append_child(elem);
306 elem_ref.from_reader_inner(reader)?;
307 },
308 ReaderEvent::EndElement { .. } => {
309 // TODO: may want to check whether we're closing the correct element
310 return Ok(());
311 },
312 ReaderEvent::Characters(s) => {
313 self.append_text_node(s);
314 },
315 ReaderEvent::CData(s) => {
316 self.append_text_node(s);
317 },
318 ReaderEvent::EndDocument => {
319 return Err(Error::EndOfDocument);
320 },
321 _ => (), // TODO: may need to implement more
322 }
323 }
324 }
325
326 pub fn write_to<W: Write>(&self, writer: &mut EventWriter<W>) -> Result<(), Error> {
327 let name = if let Some(ref ns) = self.namespace {
328 Name::qualified(&self.name, &ns, None)
329 }
330 else {
331 Name::local(&self.name)
332 };
333 let mut start = WriterEvent::start_element(name);
334 if let Some(ref ns) = self.namespace {
335 start = start.default_ns(ns.as_ref());
336 }
337 for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently
338 start = start.attr(Name::local(&attr.name), &attr.value);
339 }
340 writer.write(start)?;
341 for child in &self.children {
342 match *child {
343 Fork::Element(ref e) => {
344 e.write_to(writer)?;
345 },
346 Fork::Text(ref s) => {
347 writer.write(WriterEvent::characters(s))?;
348 },
349 }
350 }
351 writer.write(WriterEvent::end_element())?;
352 Ok(())
353 }
354
355 /// Returns an iterator over references to the children of this element.
356 ///
357 /// # Examples
358 ///
359 /// ```
360 /// use minidom::Element;
361 ///
362 /// let elem: Element = "<root><child1 /><child2 /><child3 /></root>".parse().unwrap();
363 ///
364 /// let mut iter = elem.children();
365 /// assert_eq!(iter.next().unwrap().name(), "child1");
366 /// assert_eq!(iter.next().unwrap().name(), "child2");
367 /// assert_eq!(iter.next().unwrap().name(), "child3");
368 /// assert_eq!(iter.next(), None);
369 /// ```
370 pub fn children<'a>(&'a self) -> Children<'a> {
371 Children {
372 iter: self.children.iter(),
373 }
374 }
375
376 /// Returns an iterator over mutable references to the children of this element.
377 pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> {
378 ChildrenMut {
379 iter: self.children.iter_mut(),
380 }
381 }
382
383 /// Appends a child node to the `Element`, returning the appended node.
384 ///
385 /// # Examples
386 ///
387 /// ```
388 /// use minidom::Element;
389 ///
390 /// let mut elem = Element::bare("root");
391 ///
392 /// assert_eq!(elem.children().count(), 0);
393 ///
394 /// elem.append_child(Element::bare("child"));
395 ///
396 /// {
397 /// let mut iter = elem.children();
398 /// assert_eq!(iter.next().unwrap().name(), "child");
399 /// assert_eq!(iter.next(), None);
400 /// }
401 ///
402 /// let child = elem.append_child(Element::bare("new"));
403 ///
404 /// assert_eq!(child.name(), "new");
405 /// ```
406 pub fn append_child(&mut self, mut child: Element) -> &mut Element {
407 if child.namespace.is_none() {
408 child.namespace = self.namespace.clone();
409 }
410 self.children.push(Fork::Element(child));
411 if let Fork::Element(ref mut cld) = *self.children.last_mut().unwrap() {
412 cld
413 }
414 else {
415 unreachable!()
416 }
417 }
418
419 /// Appends a text node to an `Element`.
420 ///
421 /// # Examples
422 ///
423 /// ```
424 /// use minidom::Element;
425 ///
426 /// let mut elem = Element::bare("node");
427 ///
428 /// assert_eq!(elem.text(), "");
429 ///
430 /// elem.append_text_node("text");
431 ///
432 /// assert_eq!(elem.text(), "text");
433 /// ```
434 pub fn append_text_node<S: Into<String>>(&mut self, child: S) {
435 self.children.push(Fork::Text(child.into()));
436 }
437
438 /// Returns the concatenation of all text nodes in the `Element`.
439 ///
440 /// # Examples
441 ///
442 /// ```
443 /// use minidom::Element;
444 ///
445 /// let elem: Element = "<node>hello, world!</node>".parse().unwrap();
446 ///
447 /// assert_eq!(elem.text(), "hello, world!");
448 /// ```
449 pub fn text(&self) -> String {
450 let mut ret = String::new();
451 for fork in &self.children {
452 if let Fork::Text(ref s) = *fork {
453 ret += s;
454 }
455 }
456 ret
457 }
458
459 /// Returns a reference to the first child element with the specific name and namespace, if it
460 /// exists in the direct descendants of this `Element`, else returns `None`.
461 ///
462 /// # Examples
463 ///
464 /// ```
465 /// use minidom::Element;
466 ///
467 /// let elem: Element = r#"<node xmlns="ns"><a /><a xmlns="other_ns" /><b /></node>"#.parse().unwrap();
468 ///
469 /// assert!(elem.get_child("a", "ns").unwrap().is("a", "ns"));
470 /// assert!(elem.get_child("a", "other_ns").unwrap().is("a", "other_ns"));
471 /// assert!(elem.get_child("b", "ns").unwrap().is("b", "ns"));
472 /// assert_eq!(elem.get_child("c", "ns"), None);
473 /// assert_eq!(elem.get_child("b", "other_ns"), None);
474 /// assert_eq!(elem.get_child("a", "inexistent_ns"), None);
475 /// ```
476 pub fn get_child<N: AsRef<str>, NS: AsRef<str>>(&self, name: N, namespace: NS) -> Option<&Element> {
477 for fork in &self.children {
478 if let Fork::Element(ref e) = *fork {
479 if e.is(name.as_ref(), namespace.as_ref()) {
480 return Some(e);
481 }
482 }
483 }
484 None
485 }
486
487 /// Returns a mutable reference to the first child element with the specific name and namespace,
488 /// if it exists in the direct descendants of this `Element`, else returns `None`.
489 pub fn get_child_mut<N: AsRef<str>, NS: AsRef<str>>(&mut self, name: N, namespace: NS) -> Option<&mut Element> {
490 for fork in &mut self.children {
491 if let Fork::Element(ref mut e) = *fork {
492 if e.is(name.as_ref(), namespace.as_ref()) {
493 return Some(e);
494 }
495 }
496 }
497 None
498 }
499
500 /// Returns whether a specific child with this name and namespace exists in the direct
501 /// descendants of the `Element`.
502 ///
503 /// # Examples
504 ///
505 /// ```
506 /// use minidom::Element;
507 ///
508 /// let elem: Element = r#"<node xmlns="ns"><a /><a xmlns="other_ns" /><b /></node>"#.parse().unwrap();
509 ///
510 /// assert_eq!(elem.has_child("a", "other_ns"), true);
511 /// assert_eq!(elem.has_child("a", "ns"), true);
512 /// assert_eq!(elem.has_child("a", "inexistent_ns"), false);
513 /// assert_eq!(elem.has_child("b", "ns"), true);
514 /// assert_eq!(elem.has_child("b", "other_ns"), false);
515 /// assert_eq!(elem.has_child("b", "inexistent_ns"), false);
516 /// ```
517 pub fn has_child<N: AsRef<str>, NS: AsRef<str>>(&self, name: N, namespace: NS) -> bool {
518 self.get_child(name, namespace).is_some()
519 }
520}
521
522/// An iterator over references to children of an `Element`.
523pub struct Children<'a> {
524 iter: slice::Iter<'a, Fork>,
525}
526
527impl<'a> Iterator for Children<'a> {
528 type Item = &'a Element;
529
530 fn next(&mut self) -> Option<&'a Element> {
531 while let Some(item) = self.iter.next() {
532 if let Fork::Element(ref child) = *item {
533 return Some(child);
534 }
535 }
536 None
537 }
538}
539
540/// An iterator over mutable references to children of an `Element`.
541pub struct ChildrenMut<'a> {
542 iter: slice::IterMut<'a, Fork>,
543}
544
545impl<'a> Iterator for ChildrenMut<'a> {
546 type Item = &'a mut Element;
547
548 fn next(&mut self) -> Option<&'a mut Element> {
549 while let Some(item) = self.iter.next() {
550 if let Fork::Element(ref mut child) = *item {
551 return Some(child);
552 }
553 }
554 None
555 }
556}
557
558/// A builder for `Element`s.
559pub struct ElementBuilder {
560 name: String,
561 text: Option<String>,
562 namespace: Option<String>,
563 attributes: Vec<Attribute>,
564}
565
566impl ElementBuilder {
567 /// Sets the namespace.
568 pub fn ns<S: Into<String>>(mut self, namespace: S) -> ElementBuilder {
569 self.namespace = Some(namespace.into());
570 self
571 }
572
573 /// Sets an attribute.
574 pub fn attr<S: Into<String>, V: Into<String>>(mut self, name: S, value: V) -> ElementBuilder {
575 self.attributes.push(Attribute::new(name, value));
576 self
577 }
578
579 /// Sets the inner text.
580 pub fn text<S: Into<String>>(mut self, text: S) -> ElementBuilder {
581 self.text = Some(text.into());
582 self
583 }
584
585 /// Builds the `Element`.
586 pub fn build(self) -> Element {
587 let mut elem = Element::new(self.name, self.namespace, self.attributes);
588 if let Some(text) = self.text {
589 elem.append_text_node(text);
590 }
591 elem
592 }
593}
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598
599 use xml::reader::EventReader;
600 use xml::writer::EventWriter;
601
602 const TEST_STRING: &'static str = r#"<?xml version="1.0" encoding="utf-8"?><root xmlns="root_ns" a="b">meow<child c="d" /><child xmlns="child_ns" d="e" />nya</root>"#;
603
604 fn build_test_tree() -> Element {
605 let mut root = Element::builder("root")
606 .ns("root_ns")
607 .attr("a", "b")
608 .build();
609 root.append_text_node("meow");
610 let child = Element::builder("child")
611 .attr("c", "d")
612 .build();
613 root.append_child(child);
614 let other_child = Element::builder("child")
615 .ns("child_ns")
616 .attr("d", "e")
617 .build();
618 root.append_child(other_child);
619 root.append_text_node("nya");
620 root
621 }
622
623 #[test]
624 fn reader_works() {
625 use std::io::Cursor;
626 let mut reader = EventReader::new(Cursor::new(TEST_STRING));
627 assert_eq!(Element::from_reader(&mut reader).unwrap(), build_test_tree());
628 }
629
630 #[test]
631 fn writer_works() {
632 let root = build_test_tree();
633 let mut out = Vec::new();
634 {
635 let mut writer = EventWriter::new(&mut out);
636 root.write_to(&mut writer).unwrap();
637 }
638 assert_eq!(String::from_utf8(out).unwrap(), TEST_STRING);
639 }
640
641 #[test]
642 fn builder_works() {
643 let elem = Element::builder("a")
644 .ns("b")
645 .attr("c", "d")
646 .text("e")
647 .build();
648 assert_eq!(elem.name(), "a");
649 assert_eq!(elem.ns(), Some("b"));
650 assert_eq!(elem.attr("c"), Some("d"));
651 assert_eq!(elem.attr("x"), None);
652 assert_eq!(elem.text(), "e");
653 assert!(elem.is("a", "b"));
654 }
655
656 #[test]
657 fn children_iter_works() {
658 let root = build_test_tree();
659 let mut iter = root.children();
660 assert!(iter.next().unwrap().is("child", "root_ns"));
661 assert!(iter.next().unwrap().is("child", "child_ns"));
662 assert_eq!(iter.next(), None);
663 }
664
665 #[test]
666 fn get_child_works() {
667 let root = build_test_tree();
668 assert_eq!(root.get_child("child", "inexistent_ns"), None);
669 assert_eq!(root.get_child("not_a_child", "root_ns"), None);
670 assert!(root.get_child("child", "root_ns").unwrap().is("child", "root_ns"));
671 assert!(root.get_child("child", "child_ns").unwrap().is("child", "child_ns"));
672 assert_eq!(root.get_child("child", "root_ns").unwrap().attr("c"), Some("d"));
673 assert_eq!(root.get_child("child", "child_ns").unwrap().attr("d"), Some("e"));
674 }
675}