From a01cf553aed8da17cd0b403dff5b1a289841fa68 Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 28 Jul 2017 23:58:03 +0200 Subject: [PATCH] switch Element namespaces to NamespaceSet with parent reference --- src/element.rs | 271 +++++++++++++++++++++++++++++++++++-------------- src/error.rs | 5 + src/tests.rs | 21 ++-- 3 files changed, 211 insertions(+), 86 deletions(-) diff --git a/src/element.rs b/src/element.rs index db41fd55..5aecfa86 100644 --- a/src/element.rs +++ b/src/element.rs @@ -4,6 +4,8 @@ use std::io:: Write; use std::collections::{btree_map, BTreeMap}; use std::str; +use std::cell::RefCell; +use std::rc::Rc; use error::{Error, ErrorKind, Result}; @@ -84,9 +86,9 @@ impl Node { } } - fn write_to_inner(&self, writer: &mut W, last_namespace: &mut Option) -> Result<()>{ + fn write_to_inner(&self, writer: &mut W) -> Result<()>{ match *self { - Node::Element(ref elmt) => elmt.write_to_inner(writer, last_namespace)?, + Node::Element(ref elmt) => elmt.write_to_inner(writer)?, Node::Text(ref s) => write_escaped(writer, s)?, } @@ -94,11 +96,109 @@ impl Node { } } +#[derive(Clone, Debug, PartialEq, Eq)] +struct NamespaceSet { + parent: RefCell>>, + namespaces: BTreeMap, String>, +} + +impl Default for NamespaceSet { + fn default() -> Self { + NamespaceSet { + parent: RefCell::new(None), + namespaces: BTreeMap::new(), + } + } +} + +impl NamespaceSet { + fn get(&self, prefix: &Option) -> Option { + match self.namespaces.get(prefix) { + Some(ns) => Some(ns.clone()), + None => match *self.parent.borrow() { + None => None, + Some(ref parent) => parent.get(prefix) + }, + } + } + + fn has>(&self, prefix: &Option, wanted_ns: NS) -> bool { + match self.namespaces.get(prefix) { + Some(ns) => + ns == wanted_ns.as_ref(), + None => match *self.parent.borrow() { + None => + false, + Some(ref parent) => + parent.has(prefix, wanted_ns), + }, + } + } + + fn set_parent(&self, parent: &Element) { + let mut parent_ns = self.parent.borrow_mut(); + let new_set = ((&parent.namespaces) as &Rc).clone(); + *parent_ns = Some(new_set); + } + +} + +impl From, String>> for NamespaceSet { + fn from(namespaces: BTreeMap, String>) -> Self { + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From> for NamespaceSet { + fn from(namespace: Option) -> Self { + match namespace { + None => Self::default(), + Some(namespace) => Self::from(namespace), + } + } +} + +impl From for NamespaceSet { + fn from(namespace: String) -> Self { + let mut namespaces = BTreeMap::new(); + namespaces.insert(None, namespace); + + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From<(Option, String)> for NamespaceSet { + fn from(prefix_namespace: (Option, String)) -> Self { + let (prefix, namespace) = prefix_namespace; + let mut namespaces = BTreeMap::new(); + namespaces.insert(prefix, namespace); + + NamespaceSet { + parent: RefCell::new(None), + namespaces: namespaces, + } + } +} + +impl From<(String, String)> for NamespaceSet { + fn from(prefix_namespace: (String, String)) -> Self { + let (prefix, namespace) = prefix_namespace; + Self::from((Some(prefix), namespace)) + } +} + #[derive(Clone, PartialEq, Eq, Debug)] /// A struct representing a DOM Element. pub struct Element { + prefix: Option, name: String, - namespace: Option, + namespaces: Rc, attributes: BTreeMap, children: Vec, } @@ -122,10 +222,10 @@ impl FromStr for Element { } impl Element { - fn new(name: String, namespace: Option, attributes: BTreeMap, children: Vec) -> Element { + fn new>(name: String, prefix: Option, namespaces: NS, attributes: BTreeMap, children: Vec) -> Element { Element { - name: name, - namespace: namespace, + prefix, name, + namespaces: Rc::new(namespaces.into()), attributes: attributes, children: children, } @@ -145,14 +245,16 @@ impl Element { /// .build(); /// /// assert_eq!(elem.name(), "name"); - /// assert_eq!(elem.ns(), Some("namespace")); + /// assert_eq!(elem.ns(), Some("namespace".to_owned())); /// assert_eq!(elem.attr("name"), Some("value")); /// assert_eq!(elem.attr("inexistent"), None); /// assert_eq!(elem.text(), "inner"); /// ``` - pub fn builder>(name: S) -> ElementBuilder { + pub fn builder>(name: S) -> ElementBuilder { + let (prefix, name) = split_element_name(name).unwrap(); ElementBuilder { - root: Element::new(name.into(), None, BTreeMap::new(), Vec::new()), + root: Element::new(name, prefix, None, BTreeMap::new(), Vec::new()), + namespaces: Default::default(), } } @@ -172,8 +274,9 @@ impl Element { /// ``` pub fn bare>(name: S) -> Element { Element { + prefix: None, name: name.into(), - namespace: None, + namespaces: Rc::new(NamespaceSet::default()), attributes: BTreeMap::new(), children: Vec::new(), } @@ -185,9 +288,8 @@ impl Element { } /// Returns a reference to the namespace of this element, if it has one, else `None`. - pub fn ns(&self) -> Option<&str> { - self.namespace.as_ref() - .map(String::as_ref) + pub fn ns(&self) -> Option { + self.namespaces.get(&self.prefix) } /// Returns a reference to the value of the given attribute, if it exists, else `None`. @@ -256,8 +358,8 @@ impl Element { /// assert_eq!(elem.is("wrong", "wrong"), false); /// ``` pub fn is, NS: AsRef>(&self, name: N, namespace: NS) -> bool { - let ns = self.namespace.as_ref().map(String::as_ref); - self.name == name.as_ref() && ns == Some(namespace.as_ref()) + self.name == name.as_ref() && + self.namespaces.has(&self.prefix, namespace) } /// Parse a document from an `EventReader`. @@ -322,27 +424,30 @@ impl Element { /// Output a document to a `Writer`. pub fn write_to(&self, writer: &mut W) -> Result<()> { - let mut last_namespace = None; write!(writer, "")?; - self.write_to_inner(writer, &mut last_namespace) + self.write_to_inner(writer) } - /// Output a document to a `Writer` assuming you're already in the provided namespace - pub fn write_to_in_namespace(&self, writer: &mut W, namespace: &str) -> Result<()> { - write!(writer, "")?; - self.write_to_inner(writer, &mut Some(namespace.to_owned())) - } + /// Like `write_to()` but without the `` prelude + pub fn write_to_inner(&self, writer: &mut W) -> Result<()> { + let name = match &self.prefix { + &None => self.name.to_owned(), + &Some(ref prefix) => format!("{}:{}", prefix, self.name), + }; + write!(writer, "<{}", name)?; - fn write_to_inner(&self, writer: &mut W, last_namespace: &mut Option) -> Result<()> { - write!(writer, "<")?; - write!(writer, "{}", self.name)?; - - if let Some(ref ns) = self.namespace { - if *last_namespace != self.namespace { - write!(writer, " xmlns=\"")?; - write_escaped(writer, ns)?; - write!(writer, "\"")?; - *last_namespace = Some(ns.clone()); + for (prefix, ns) in &self.namespaces.namespaces { + match prefix { + &None => { + write!(writer, " xmlns=\"")?; + write_escaped(writer, ns)?; + write!(writer, "\"")?; + }, + &Some(ref prefix) => { + write!(writer, " xmlns:{}=\"", prefix)?; + write_escaped(writer, ns)?; + write!(writer, "\"")?; + }, } } @@ -360,10 +465,10 @@ impl Element { write!(writer, ">")?; for child in &self.children { - child.write_to_inner(writer, last_namespace)?; + child.write_to_inner(writer)?; } - write!(writer, "", self.name)?; + write!(writer, "", name)?; Ok(()) } @@ -472,30 +577,17 @@ impl Element { /// /// assert_eq!(child.name(), "new"); /// ``` - pub fn append_child(&mut self, mut child: Element) -> &mut Element { - if child.namespace.is_none() && self.namespace.is_some() { - child.namespace = self.namespace.clone(); - child.propagate_namespaces(); - } + pub fn append_child(&mut self, child: Element) -> &mut Element { + child.namespaces.set_parent(&self); + self.children.push(Node::Element(child)); if let Node::Element(ref mut cld) = *self.children.last_mut().unwrap() { cld - } - else { + } else { unreachable!() } } - 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 @@ -610,29 +702,42 @@ impl Element { } } -fn build_element(event: &BytesStart) -> Result { - let mut attributes = event.attributes() - .map(|o| { - let o = o?; - let key = str::from_utf8(o.key)?.to_owned(); - let value = str::from_utf8(o.value)?.to_owned(); - Ok((key, value)) - } - ) - .collect::>>()?; - let mut ns_key = None; - for (key, _) in &attributes { - if key == "xmlns" || key.starts_with("xmlns:") { - ns_key = Some(key.clone()); - } - } +fn split_element_name>(s: S) -> Result<(Option, String)> { + let name_parts = s.as_ref().split(':').collect::>(); + match name_parts.len() { + 2 => Ok((Some(name_parts[0].to_owned()), name_parts[1].to_owned())), + 1 => Ok((None, name_parts[0].to_owned())), + _ => bail!(ErrorKind::InvalidElement), + } +} - let ns = match ns_key { - None => None, - Some(key) => attributes.remove(&key), - }; - let name = str::from_utf8(event.name())?.to_owned(); - Ok(Element::new(name, ns, attributes, Vec::new())) +fn build_element(event: &BytesStart) -> Result { + let mut namespaces = BTreeMap::new(); + let attributes = event.attributes() + .map(|o| { + let o = o?; + let key = str::from_utf8(o.key)?.to_owned(); + let value = str::from_utf8(o.value)?.to_owned(); + Ok((key, value)) + }) + .filter(|o| { + match o { + &Ok((ref key, ref value)) if key == "xmlns" => { + namespaces.insert(None, value.to_owned()); + false + }, + &Ok((ref key, ref value)) if key.starts_with("xmlns:") => { + namespaces.insert(Some(key[6..].to_owned()), value.to_owned()); + false + }, + _ => true, + } + }) + .collect::>>()?; + + let (prefix, name) = split_element_name(str::from_utf8(event.name())?)?; + let element = Element::new(name, prefix, namespaces, attributes, Vec::new()); + Ok(element) } /// An iterator over references to child elements of an `Element`. @@ -742,12 +847,14 @@ impl<'a> Iterator for AttrsMut<'a> { /// A builder for `Element`s. pub struct ElementBuilder { root: Element, + namespaces: BTreeMap, String>, } impl ElementBuilder { /// Sets the namespace. pub fn ns>(mut self, namespace: S) -> ElementBuilder { - self.root.namespace = Some(namespace.into()); + self.namespaces + .insert(self.root.prefix.clone(), namespace.into()); self } @@ -768,21 +875,33 @@ impl ElementBuilder { /// Builds the `Element`. pub fn build(self) -> Element { - self.root + let mut element = self.root; + // Set namespaces + element.namespaces = Rc::new(NamespaceSet::from(self.namespaces)); + // Propagate namespaces + for node in &element.children { + if let Node::Element(ref e) = *node { + e.namespaces.set_parent(&element); + } + } + + element } } +#[cfg(test)] #[test] fn test_element_new() { use std::iter::FromIterator; let elem = Element::new( "name".to_owned() + , None , Some("namespace".to_owned()) , BTreeMap::from_iter(vec![ ("name".to_string(), "value".to_string()) ].into_iter() ) , Vec::new() ); assert_eq!(elem.name(), "name"); - assert_eq!(elem.ns(), Some("namespace")); + assert_eq!(elem.ns(), Some("namespace".to_owned())); assert_eq!(elem.attr("name"), Some("value")); assert_eq!(elem.attr("inexistent"), None); } diff --git a/src/error.rs b/src/error.rs index 0b8eb239..4079365f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -26,5 +26,10 @@ error_chain! { description("The XML is invalid, an element was wrongly closed") display("the XML is invalid, an element was wrongly closed") } + /// An error which is returned when an elemet's name contains more than one colon + InvalidElement { + description("The XML element is invalid") + display("the XML element is invalid") + } } } diff --git a/src/tests.rs b/src/tests.rs index 40558eca..e7877158 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -80,7 +80,7 @@ fn builder_works() { .append("e") .build(); assert_eq!(elem.name(), "a"); - assert_eq!(elem.ns(), Some("b")); + assert_eq!(elem.ns(), Some("b".to_owned())); assert_eq!(elem.attr("c"), Some("d")); assert_eq!(elem.attr("x"), None); assert_eq!(elem.text(), "e"); @@ -115,6 +115,7 @@ fn namespace_propagation_works() { let grandchild = Element::bare("grandchild"); child.append_child(grandchild); root.append_child(child); + assert_eq!(root.get_child("child", "root_ns").unwrap().ns(), root.ns()); assert_eq!(root.get_child("child", "root_ns").unwrap() .get_child("grandchild", "root_ns").unwrap() @@ -154,7 +155,7 @@ fn wrongly_closed_elements_error() { fn namespace_simple() { let elem: Element = "".parse().unwrap(); assert_eq!(elem.name(), "message"); - assert_eq!(elem.ns(), Some("jabber:client")); + assert_eq!(elem.ns(), Some("jabber:client".to_owned())); } #[test] @@ -162,28 +163,28 @@ fn namespace_prefixed() { let elem: Element = "" .parse().unwrap(); assert_eq!(elem.name(), "features"); - assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams".to_owned())); } #[test] fn namespace_inherited_simple() { let elem: Element = "".parse().unwrap(); assert_eq!(elem.name(), "stream"); - assert_eq!(elem.ns(), Some("jabber:client")); + assert_eq!(elem.ns(), Some("jabber:client".to_owned())); let child = elem.children().next().unwrap(); assert_eq!(child.name(), "message"); - assert_eq!(child.ns(), Some("jabber:client")); + assert_eq!(child.ns(), Some("jabber:client".to_owned())); } #[test] fn namespace_inherited_prefixed1() { - let elem: Element = "" + let elem: Element = "" .parse().unwrap(); assert_eq!(elem.name(), "features"); - assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams".to_owned())); let child = elem.children().next().unwrap(); assert_eq!(child.name(), "message"); - assert_eq!(child.ns(), Some("jabber:client")); + assert_eq!(child.ns(), Some("jabber:client".to_owned())); } #[test] @@ -191,8 +192,8 @@ fn namespace_inherited_prefixed2() { let elem: Element = "" .parse().unwrap(); assert_eq!(elem.name(), "stream"); - assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams".to_owned())); let child = elem.children().next().unwrap(); assert_eq!(child.name(), "message"); - assert_eq!(child.ns(), Some("jabber:client")); + assert_eq!(child.ns(), Some("jabber:client".to_owned())); }