Get rid of IntoElements, replace it with Into<Node> and <T: Into<Node> IntoIterator<Item = T>. 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`.
This commit is contained in:
lumi 2018-12-23 15:40:23 +01:00
parent da7de97dce
commit c5c8dee20a
3 changed files with 162 additions and 90 deletions

View file

@ -2,85 +2,6 @@
use element::{Element, ElementBuilder}; 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<T: IntoElements> IntoElements for Vec<T> {
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<T: IntoElements> IntoElements for Option<T> {
fn into_elements(self, emitter: &mut ElementEmitter) {
if let Some(e) = self {
e.into_elements(emitter);
}
}
}
impl<T> IntoElements for T where T: Into<Element> {
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. /// A trait for types which can be converted to an attribute value.
pub trait IntoAttributeValue { pub trait IntoAttributeValue {
/// Turns this into an attribute string, or None if it shouldn't be added. /// Turns this into an attribute string, or None if it shouldn't be added.

View file

@ -19,7 +19,7 @@ use std::str::FromStr;
use std::slice; use std::slice;
use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; use convert::IntoAttributeValue;
use namespace_set::NamespaceSet; use namespace_set::NamespaceSet;
/// helper function to escape a `&[u8]` and replace all /// helper function to escape a `&[u8]` and replace all
@ -80,7 +80,8 @@ pub enum Node {
} }
impl 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 /// # 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("<meow />".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("<meow />".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<Element> {
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 /// # 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("<meow />".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("<meow />".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<String> {
match self {
Node::Element(_) => None,
Node::Text(s) => Some(s),
Node::Comment(_) => None,
}
}
fn write_to_inner<W: Write>(&self, writer: &mut EventWriter<W>) -> Result<()>{ fn write_to_inner<W: Write>(&self, writer: &mut EventWriter<W>) -> Result<()>{
match *self { match *self {
Node::Element(ref elmt) => elmt.write_to_inner(writer)?, Node::Element(ref elmt) => elmt.write_to_inner(writer)?,
@ -139,6 +235,30 @@ impl Node {
} }
} }
impl From<Element> for Node {
fn from(elm: Element) -> Node {
Node::Element(elm)
}
}
impl From<String> 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<ElementBuilder> for Node {
fn from(builder: ElementBuilder) -> Node {
Node::Element(builder.build())
}
}
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
/// A struct representing a DOM Element. /// A struct representing a DOM Element.
pub struct Element { pub struct Element {
@ -157,7 +277,6 @@ impl<'a> From<&'a Element> for String {
} }
} }
impl FromStr for Element { impl FromStr for Element {
type Err = Error; type Err = Error;
@ -736,6 +855,34 @@ impl Element {
pub fn has_child<N: AsRef<str>, NS: AsRef<str>>(&self, name: N, namespace: NS) -> bool { pub fn has_child<N: AsRef<str>, NS: AsRef<str>>(&self, name: N, namespace: NS) -> bool {
self.get_child(name, namespace).is_some() self.get_child(name, namespace).is_some()
} }
/// Removes the first child with this name and namespace, if it exists, and returns an
/// `Option<Element>` 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#"<node xmlns="ns"><a /><a xmlns="other_ns" /><b /></node>"#.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<N: AsRef<str>, NS: AsRef<str>>(&mut self, name: N, namespace: NS) -> Option<Element> {
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: AsRef<str>>(s: S) -> Result<(Option<String>, String)> { fn split_element_name<S: AsRef<str>>(s: S) -> Result<(Option<String>, String)> {
@ -900,11 +1047,16 @@ impl ElementBuilder {
self self
} }
/// Appends anything implementing `IntoElements` into the tree. /// Appends anything implementing `Into<Node>` into the tree.
pub fn append<T: IntoElements>(mut self, into: T) -> ElementBuilder { pub fn append<T: Into<Node>>(mut self, node: T) -> ElementBuilder {
{ self.root.append_node(node.into());
let mut emitter = ElementEmitter::new(&mut self.root); self
into.into_elements(&mut emitter); }
/// Appends an iterator of things implementing `Into<Node>` into the tree.
pub fn append_all<T: Into<Node>, I: IntoIterator<Item = T>>(mut self, iter: I) -> ElementBuilder {
for node in iter {
self.root.append_node(node.into());
} }
self self
} }
@ -920,7 +1072,6 @@ impl ElementBuilder {
e.namespaces.set_parent(Rc::clone(&element.namespaces)); e.namespaces.set_parent(Rc::clone(&element.namespaces));
} }
} }
element element
} }
} }

View file

@ -77,4 +77,4 @@ mod namespace_set;
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder};
pub use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; pub use convert::IntoAttributeValue;