From 83d80dd2353f0144da76aea95153103c33f7c8f0 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 14 May 2017 16:38:56 +0200 Subject: [PATCH] Add iterators over attributes, nodes and text nodes. Clean up lots of code. Add as_element and as_text on Node. Add some inline annotations. --- src/element.rs | 268 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 227 insertions(+), 41 deletions(-) diff --git a/src/element.rs b/src/element.rs index 5ac2a144..71bd3911 100644 --- a/src/element.rs +++ b/src/element.rs @@ -3,6 +3,7 @@ use std::io::prelude::*; use std::io::Cursor; use std::collections::BTreeMap; +use std::collections::btree_map; use std::fmt; @@ -19,6 +20,57 @@ use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +/// A node in an element tree. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Node { + /// An `Element`. + Element(Element), + /// A text node. + Text(String), +} + +impl Node { + /// Turns this into an `Element` if possible, else returns None. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_element().unwrap().name(), "meow"); + /// assert_eq!(txt.as_element(), None); + /// ``` + pub fn as_element<'a>(&'a self) -> Option<&'a Element> { + match *self { + Node::Element(ref e) => Some(e), + Node::Text(_) => None, + } + } + + /// Turns this into a `String` if possible, else returns None. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_text(), None); + /// assert_eq!(txt.as_text().unwrap(), "meow"); + /// ``` + pub fn as_text<'a>(&'a self) -> Option<&'a str> { + match *self { + Node::Element(_) => None, + Node::Text(ref s) => Some(s), + } + } +} + #[derive(Clone, PartialEq, Eq)] /// A struct representing a DOM Element. pub struct Element { @@ -28,7 +80,6 @@ pub struct Element { children: Vec, } - impl<'a> From<&'a Element> for String { fn from(elem: &'a Element) -> String { let mut out = Vec::new(); @@ -55,15 +106,6 @@ impl FromStr for Element { } } -/// A node in an element tree. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Node { - /// An `Element`. - Element(Element), - /// A text node. - Text(String), -} - impl Element { fn new(name: String, namespace: Option, attributes: BTreeMap, children: Vec) -> Element { Element { @@ -78,7 +120,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let elem = Element::builder("name") @@ -103,7 +145,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let bare = Element::bare("name"); @@ -141,6 +183,34 @@ impl Element { None } + /// Returns an iterator over the attributes of this element. + /// + /// # Example + /// + /// ```rust + /// use minidom::Element; + /// + /// let elm: Element = "".parse().unwrap(); + /// + /// let mut iter = elm.attrs(); + /// + /// assert_eq!(iter.next().unwrap(), ("a", "b")); + /// assert_eq!(iter.next(), None); + /// ``` + pub fn attrs<'a>(&'a self) -> Attrs<'a> { + Attrs { + iter: self.attributes.iter(), + } + } + + /// Returns an iterator over the attributes of this element, with the value being a mutable + /// reference. + pub fn attrs_mut<'a>(&'a mut self) -> AttrsMut<'a> { + AttrsMut { + iter: self.attributes.iter_mut(), + } + } + /// Modifies the value of an attribute. pub fn set_attr, V: IntoAttributeValue>(&mut self, name: S, val: V) { let name = name.into(); @@ -160,7 +230,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let elem = Element::builder("name").ns("namespace").build(); @@ -281,14 +351,41 @@ impl Element { Ok(()) } - /// Returns an iterator over references to the children of this element. + /// Returns an iterator over references to every child node of this element. /// /// # Examples /// + /// ```rust + /// use minidom::{Element, Node}; + /// + /// let elem: Element = "abc".parse().unwrap(); + /// + /// let mut iter = elem.nodes(); + /// + /// assert_eq!(iter.next().unwrap().as_text().unwrap(), "a"); + /// assert_eq!(iter.next().unwrap().as_element().unwrap().name(), "c1"); + /// assert_eq!(iter.next().unwrap().as_text().unwrap(), "b"); + /// assert_eq!(iter.next().unwrap().as_element().unwrap().name(), "c2"); + /// assert_eq!(iter.next().unwrap().as_text().unwrap(), "c"); + /// assert_eq!(iter.next(), None); /// ``` + #[inline] pub fn nodes<'a>(&'a self) -> Nodes<'a> { + self.children.iter() + } + + /// Returns an iterator over mutable references to every child node of this element. + #[inline] pub fn nodes_mut<'a>(&'a mut self) -> NodesMut<'a> { + self.children.iter_mut() + } + + /// Returns an iterator over references to every child element of this element. + /// + /// # Examples + /// + /// ```rust /// use minidom::Element; /// - /// let elem: Element = "".parse().unwrap(); + /// let elem: Element = "hellothisisignored".parse().unwrap(); /// /// let mut iter = elem.children(); /// assert_eq!(iter.next().unwrap().name(), "child1"); @@ -296,26 +393,43 @@ impl Element { /// assert_eq!(iter.next().unwrap().name(), "child3"); /// assert_eq!(iter.next(), None); /// ``` - pub fn children<'a>(&'a self) -> Children<'a> { + #[inline] pub fn children<'a>(&'a self) -> Children<'a> { Children { iter: self.children.iter(), } } - /// Returns an iterator over mutable references to the children of this element. - pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { + /// Returns an iterator over mutable references to every child element of this element. + #[inline] pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { ChildrenMut { iter: self.children.iter_mut(), } } - fn propagate_namespaces(&mut self) { - let ns = self.namespace.clone(); - for child in self.children_mut() { - if child.namespace.is_none() { - child.namespace = ns.clone(); - child.propagate_namespaces(); - } + /// Returns an iterator over references to every text node of this element. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Element; + /// + /// let elem: Element = "hello world!".parse().unwrap(); + /// + /// let mut iter = elem.texts(); + /// assert_eq!(iter.next().unwrap(), "hello"); + /// assert_eq!(iter.next().unwrap(), " world!"); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] pub fn texts<'a>(&'a self) -> Texts<'a> { + Texts { + iter: self.children.iter(), + } + } + + /// Returns an iterator over mutable references to every text node of this element. + #[inline] pub fn texts_mut<'a>(&'a mut self) -> TextsMut<'a> { + TextsMut { + iter: self.children.iter_mut(), } } @@ -323,7 +437,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let mut elem = Element::bare("root"); @@ -356,11 +470,21 @@ impl Element { } } + fn propagate_namespaces(&mut self) { + let ns = self.namespace.clone(); + for child in self.children_mut() { + if child.namespace.is_none() { + child.namespace = ns.clone(); + child.propagate_namespaces(); + } + } + } + /// Appends a text node to an `Element`. /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let mut elem = Element::bare("node"); @@ -379,7 +503,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::{Element, Node}; /// /// let mut elem = Element::bare("node"); @@ -396,21 +520,15 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// - /// let elem: Element = "hello, world!".parse().unwrap(); + /// let elem: Element = "hello, world!".parse().unwrap(); /// /// assert_eq!(elem.text(), "hello, world!"); /// ``` pub fn text(&self) -> String { - let mut ret = String::new(); - for fork in &self.children { - if let Node::Text(ref s) = *fork { - ret += s; - } - } - ret + self.texts().fold(String::new(), |ret, new| ret + new) } /// Returns a reference to the first child element with the specific name and namespace, if it @@ -418,7 +536,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let elem: Element = r#""#.parse().unwrap(); @@ -459,7 +577,7 @@ impl Element { /// /// # Examples /// - /// ``` + /// ```rust /// use minidom::Element; /// /// let elem: Element = r#""#.parse().unwrap(); @@ -476,7 +594,7 @@ impl Element { } } -/// An iterator over references to children of an `Element`. +/// An iterator over references to child elements of an `Element`. pub struct Children<'a> { iter: slice::Iter<'a, Node>, } @@ -494,7 +612,7 @@ impl<'a> Iterator for Children<'a> { } } -/// An iterator over mutable references to children of an `Element`. +/// An iterator over mutable references to child elements of an `Element`. pub struct ChildrenMut<'a> { iter: slice::IterMut<'a, Node>, } @@ -512,6 +630,74 @@ impl<'a> Iterator for ChildrenMut<'a> { } } +/// An iterator over references to child text nodes of an `Element`. +pub struct Texts<'a> { + iter: slice::Iter<'a, Node>, +} + +impl<'a> Iterator for Texts<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option<&'a str> { + while let Some(item) = self.iter.next() { + if let Node::Text(ref child) = *item { + return Some(child); + } + } + None + } +} + +/// An iterator over mutable references to child text nodes of an `Element`. +pub struct TextsMut<'a> { + iter: slice::IterMut<'a, Node>, +} + +impl<'a> Iterator for TextsMut<'a> { + type Item = &'a mut String; + + fn next(&mut self) -> Option<&'a mut String> { + while let Some(item) = self.iter.next() { + if let Node::Text(ref mut child) = *item { + return Some(child); + } + } + None + } +} + +/// An iterator over references to all child nodes of an `Element`. +pub type Nodes<'a> = slice::Iter<'a, Node>; + +/// An iterator over mutable references to all child nodes of an `Element`. +pub type NodesMut<'a> = slice::IterMut<'a, Node>; + +/// An iterator over the attributes of an `Element`. +pub struct Attrs<'a> { + iter: btree_map::Iter<'a, String, String>, +} + +impl<'a> Iterator for Attrs<'a> { + type Item = (&'a str, &'a str); + + fn next(&mut self) -> Option { + self.iter.next().map(|(x, y)| (x.as_ref(), y.as_ref())) + } +} + +/// An iterator over the attributes of an `Element`, with the values mutable. +pub struct AttrsMut<'a> { + iter: btree_map::IterMut<'a, String, String>, +} + +impl<'a> Iterator for AttrsMut<'a> { + type Item = (&'a str, &'a mut String); + + fn next(&mut self) -> Option { + self.iter.next().map(|(x, y)| (x.as_ref(), y)) + } +} + /// A builder for `Element`s. pub struct ElementBuilder { root: Element,