From c5c8dee20a951b6dc2b2aa9f22724a3044b5c376 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 23 Dec 2018 15:40:23 +0100 Subject: [PATCH] Get rid of `IntoElements`, replace it with `Into` and ` IntoIterator`. This is a backwards-incompatible change, but should require minimal changes. Doing this splits the `append` method in `ElementBuilder` into two methods. `append` now appends exactly one node, while `append_all` appends an iterator of nodes. Add `remove_child`, which removes the first child that has a specific name and namespace, then returns it if it exists. Add a lot of convenience methods on `Node`: `as_text_mut`, `as_element_mut`, `into_text`, `into_element`. --- src/convert.rs | 79 ----------------------- src/element.rs | 171 ++++++++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 2 +- 3 files changed, 162 insertions(+), 90 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index 1a528111..cabe86d5 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -2,85 +2,6 @@ use element::{Element, ElementBuilder}; -/// A struct which is used for emitting `Element`s and text nodes into an `Element` without -/// exposing more functionality than necessary. -pub struct ElementEmitter<'a>(&'a mut Element); - -impl<'a> ElementEmitter<'a> { - /// Creates a new `ElementEmitter`. - pub fn new(root: &'a mut Element) -> ElementEmitter<'a> { - ElementEmitter(root) - } - - /// Appends an `Element` to the target. - pub fn append_child(&mut self, element: Element) { - self.0.append_child(element); - } - - /// Appends a text node to the target. - pub fn append_text_node(&mut self, text: String) { - self.0.append_text_node(text); - } -} - -/// A trait for types which can be converted to one or multiple `Element`s. -pub trait IntoElements { - /// Emits this as a sequence of text nodes and `Element`s. - fn into_elements(self, emitter: &mut ElementEmitter); -} - -impl IntoElements for Vec { - fn into_elements(self, emitter: &mut ElementEmitter) { - for elem in self { - elem.into_elements(emitter); - } - } -} - -impl<'a, T: IntoElements + Clone> IntoElements for &'a [T] { - fn into_elements(self, emitter: &mut ElementEmitter) { - self.to_vec().into_elements(emitter); - } -} - -impl IntoElements for Option { - fn into_elements(self, emitter: &mut ElementEmitter) { - if let Some(e) = self { - e.into_elements(emitter); - } - } -} - -impl IntoElements for T where T: Into { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.into()); - } -} - -impl IntoElements for ElementBuilder { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_child(self.build()); - } -} - -impl IntoElements for String { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(self); - } -} - -impl<'a> IntoElements for &'a String { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(self.to_owned()); - } -} - -impl<'a> IntoElements for &'a str { - fn into_elements(self, emitter: &mut ElementEmitter) { - emitter.append_text_node(self.to_owned()); - } -} - /// A trait for types which can be converted to an attribute value. pub trait IntoAttributeValue { /// Turns this into an attribute string, or None if it shouldn't be added. diff --git a/src/element.rs b/src/element.rs index e147a27d..88a048d1 100644 --- a/src/element.rs +++ b/src/element.rs @@ -19,7 +19,7 @@ use std::str::FromStr; use std::slice; -use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +use convert::IntoAttributeValue; use namespace_set::NamespaceSet; /// helper function to escape a `&[u8]` and replace all @@ -80,7 +80,8 @@ pub enum Node { } impl Node { - /// Turns this into an `Element` if possible, else returns None. + /// Turns this into a reference to an `Element` if this is an element node. + /// Else this returns `None`. /// /// # Examples /// @@ -101,7 +102,52 @@ impl Node { } } - /// Turns this into a `String` if possible, else returns None. + /// Turns this into a mutable reference of an `Element` if this is an element node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let mut elm = Node::Element("".parse().unwrap()); + /// let mut txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_element_mut().unwrap().name(), "meow"); + /// assert_eq!(txt.as_element_mut(), None); + /// ``` + pub fn as_element_mut(&mut self) -> Option<&mut Element> { + match *self { + Node::Element(ref mut e) => Some(e), + Node::Text(_) => None, + Node::Comment(_) => None, + } + } + + /// Turns this into an `Element`, consuming self, if this is an element node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.into_element().unwrap().name(), "meow"); + /// assert_eq!(txt.into_element(), None); + /// ``` + pub fn into_element(self) -> Option { + match self { + Node::Element(e) => Some(e), + Node::Text(_) => None, + Node::Comment(_) => None, + } + } + + /// Turns this into an `&str` if this is a text node. + /// Else this returns `None`. /// /// # Examples /// @@ -122,6 +168,56 @@ impl Node { } } + /// Turns this into an `&mut String` if this is a text node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let mut elm = Node::Element("".parse().unwrap()); + /// let mut txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.as_text_mut(), None); + /// { + /// let text_mut = txt.as_text_mut().unwrap(); + /// assert_eq!(text_mut, "meow"); + /// text_mut.push_str("zies"); + /// assert_eq!(text_mut, "meowzies"); + /// } + /// assert_eq!(txt.as_text().unwrap(), "meowzies"); + /// ``` + pub fn as_text_mut(&mut self) -> Option<&mut String> { + match *self { + Node::Element(_) => None, + Node::Text(ref mut s) => Some(s), + Node::Comment(_) => None, + } + } + + /// Turns this into an `String`, consuming self, if this is a text node. + /// Else this returns `None`. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Node; + /// + /// let elm = Node::Element("".parse().unwrap()); + /// let txt = Node::Text("meow".to_owned()); + /// + /// assert_eq!(elm.into_text(), None); + /// assert_eq!(txt.into_text().unwrap(), "meow"); + /// ``` + pub fn into_text(self) -> Option { + match self { + Node::Element(_) => None, + Node::Text(s) => Some(s), + Node::Comment(_) => None, + } + } + fn write_to_inner(&self, writer: &mut EventWriter) -> Result<()>{ match *self { Node::Element(ref elmt) => elmt.write_to_inner(writer)?, @@ -139,6 +235,30 @@ impl Node { } } +impl From for Node { + fn from(elm: Element) -> Node { + Node::Element(elm) + } +} + +impl From for Node { + fn from(s: String) -> Node { + Node::Text(s) + } +} + +impl<'a> From<&'a str> for Node { + fn from(s: &'a str) -> Node { + Node::Text(s.to_owned()) + } +} + +impl From for Node { + fn from(builder: ElementBuilder) -> Node { + Node::Element(builder.build()) + } +} + #[derive(Clone, PartialEq, Eq, Debug)] /// A struct representing a DOM Element. pub struct Element { @@ -157,7 +277,6 @@ impl<'a> From<&'a Element> for String { } } - impl FromStr for Element { type Err = Error; @@ -736,6 +855,34 @@ impl Element { pub fn has_child, NS: AsRef>(&self, name: N, namespace: NS) -> bool { self.get_child(name, namespace).is_some() } + + /// Removes the first child with this name and namespace, if it exists, and returns an + /// `Option` containing this child if it succeeds. + /// Returns `None` if no child matches this name and namespace. + /// + /// # Examples + /// + /// ```rust + /// use minidom::Element; + /// + /// let mut elem: Element = r#""#.parse().unwrap(); + /// + /// assert!(elem.remove_child("a", "ns").unwrap().is("a", "ns")); + /// assert!(elem.remove_child("a", "ns").is_none()); + /// assert!(elem.remove_child("inexistent", "inexistent").is_none()); + /// ``` + pub fn remove_child, NS: AsRef>(&mut self, name: N, namespace: NS) -> Option { + let name = name.as_ref(); + let namespace = namespace.as_ref(); + let idx = self.children.iter().position(|x| { + if let Node::Element(ref elm) = x { + elm.is(name, namespace) + } else { + false + } + })?; + self.children.remove(idx).into_element() + } } fn split_element_name>(s: S) -> Result<(Option, String)> { @@ -900,11 +1047,16 @@ impl ElementBuilder { self } - /// Appends anything implementing `IntoElements` into the tree. - pub fn append(mut self, into: T) -> ElementBuilder { - { - let mut emitter = ElementEmitter::new(&mut self.root); - into.into_elements(&mut emitter); + /// Appends anything implementing `Into` into the tree. + pub fn append>(mut self, node: T) -> ElementBuilder { + self.root.append_node(node.into()); + self + } + + /// Appends an iterator of things implementing `Into` into the tree. + pub fn append_all, I: IntoIterator>(mut self, iter: I) -> ElementBuilder { + for node in iter { + self.root.append_node(node.into()); } self } @@ -920,7 +1072,6 @@ impl ElementBuilder { e.namespaces.set_parent(Rc::clone(&element.namespaces)); } } - element } } diff --git a/src/lib.rs b/src/lib.rs index 32a18147..20be49d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,4 +77,4 @@ mod namespace_set; pub use error::{Error, Result}; pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; -pub use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +pub use convert::IntoAttributeValue;