From 77bc215cba9a1058f5fd9b1ba4c2bb0f68cc325d Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 19 Jul 2017 01:14:33 +0200 Subject: [PATCH 1/9] tests: add namespace inheritance tests --- src/tests.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/tests.rs b/src/tests.rs index 6dd6a2b..40558ec 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -149,3 +149,50 @@ fn wrongly_closed_elements_error() { let elem1 = "".parse::(); assert!(elem1.is_ok()); } + +#[test] +fn namespace_simple() { + let elem: Element = "".parse().unwrap(); + assert_eq!(elem.name(), "message"); + assert_eq!(elem.ns(), Some("jabber:client")); +} + +#[test] +fn namespace_prefixed() { + let elem: Element = "" + .parse().unwrap(); + assert_eq!(elem.name(), "features"); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); +} + +#[test] +fn namespace_inherited_simple() { + let elem: Element = "".parse().unwrap(); + assert_eq!(elem.name(), "stream"); + assert_eq!(elem.ns(), Some("jabber:client")); + let child = elem.children().next().unwrap(); + assert_eq!(child.name(), "message"); + assert_eq!(child.ns(), Some("jabber:client")); +} + +#[test] +fn namespace_inherited_prefixed1() { + let elem: Element = "" + .parse().unwrap(); + assert_eq!(elem.name(), "features"); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + let child = elem.children().next().unwrap(); + assert_eq!(child.name(), "message"); + assert_eq!(child.ns(), Some("jabber:client")); +} + +#[test] +fn namespace_inherited_prefixed2() { + let elem: Element = "" + .parse().unwrap(); + assert_eq!(elem.name(), "stream"); + assert_eq!(elem.ns(), Some("http://etherx.jabber.org/streams")); + let child = elem.children().next().unwrap(); + assert_eq!(child.name(), "message"); + assert_eq!(child.ns(), Some("jabber:client")); +} From a01cf553aed8da17cd0b403dff5b1a289841fa68 Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 28 Jul 2017 23:58:03 +0200 Subject: [PATCH 2/9] 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 db41fd5..5aecfa8 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 0b8eb23..4079365 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 40558ec..e787715 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())); } From 26f03c27edbe798f0a77f34d4c352f4e4fd79afe Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 12 Aug 2017 02:13:32 +0200 Subject: [PATCH 3/9] optimize Element::write_to_inner() with CoW --- src/element.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/element.rs b/src/element.rs index 5aecfa8..2c16b3c 100644 --- a/src/element.rs +++ b/src/element.rs @@ -6,6 +6,7 @@ use std::collections::{btree_map, BTreeMap}; use std::str; use std::cell::RefCell; use std::rc::Rc; +use std::borrow::Cow; use error::{Error, ErrorKind, Result}; @@ -431,8 +432,8 @@ impl Element { /// 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), + &None => Cow::Borrowed(&self.name), + &Some(ref prefix) => Cow::Owned(format!("{}:{}", prefix, self.name)), }; write!(writer, "<{}", name)?; From 98fd71fd3f491a49b042ab63d0a3cc30ffbea34d Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 02:26:45 +0200 Subject: [PATCH 4/9] NamespaceSet: rm unneeded cast --- src/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/element.rs b/src/element.rs index 2c16b3c..8623131 100644 --- a/src/element.rs +++ b/src/element.rs @@ -138,7 +138,7 @@ impl NamespaceSet { fn set_parent(&self, parent: &Element) { let mut parent_ns = self.parent.borrow_mut(); - let new_set = ((&parent.namespaces) as &Rc).clone(); + let new_set = parent.namespaces.clone(); *parent_ns = Some(new_set); } From 7d2699e08e36d28997f573ca3e76ee09cabe9669 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 02:28:44 +0200 Subject: [PATCH 5/9] NamespaceSet::set_parent(): don't require a full Element by passing just a reference to a NamespaceSet we could reuse this for xmlns elision in the serialization code. --- src/element.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/element.rs b/src/element.rs index 8623131..1a9aeb8 100644 --- a/src/element.rs +++ b/src/element.rs @@ -136,9 +136,9 @@ impl NamespaceSet { } } - fn set_parent(&self, parent: &Element) { + fn set_parent(&self, parent: Rc) { let mut parent_ns = self.parent.borrow_mut(); - let new_set = parent.namespaces.clone(); + let new_set = parent; *parent_ns = Some(new_set); } @@ -579,7 +579,7 @@ impl Element { /// assert_eq!(child.name(), "new"); /// ``` pub fn append_child(&mut self, child: Element) -> &mut Element { - child.namespaces.set_parent(&self); + child.namespaces.set_parent(self.namespaces.clone()); self.children.push(Node::Element(child)); if let Node::Element(ref mut cld) = *self.children.last_mut().unwrap() { @@ -882,7 +882,7 @@ impl ElementBuilder { // Propagate namespaces for node in &element.children { if let Node::Element(ref e) = *node { - e.namespaces.set_parent(&element); + e.namespaces.set_parent(element.namespaces.clone()); } } From 0148790a0137fb480a4adbe35f3594a95354fd68 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 02:35:24 +0200 Subject: [PATCH 6/9] move NamespaceSet to namespaces mod --- src/element.rs | 101 +------------------------------------------- src/lib.rs | 1 + src/namespaces.rs | 105 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 99 deletions(-) create mode 100644 src/namespaces.rs diff --git a/src/element.rs b/src/element.rs index 1a9aeb8..47e25d7 100644 --- a/src/element.rs +++ b/src/element.rs @@ -4,7 +4,6 @@ use std::io:: Write; use std::collections::{btree_map, BTreeMap}; use std::str; -use std::cell::RefCell; use std::rc::Rc; use std::borrow::Cow; @@ -20,6 +19,7 @@ use std::str::FromStr; use std::slice; use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; +use namespaces::NamespaceSet; /// Escape XML text pub fn write_escaped(writer: &mut W, input: &str) -> Result<()> { @@ -97,103 +97,6 @@ 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: Rc) { - let mut parent_ns = self.parent.borrow_mut(); - let new_set = parent; - *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 { @@ -437,7 +340,7 @@ impl Element { }; write!(writer, "<{}", name)?; - for (prefix, ns) in &self.namespaces.namespaces { + for (prefix, ns) in self.namespaces.declared_ns() { match prefix { &None => { write!(writer, " xmlns=\"")?; diff --git a/src/lib.rs b/src/lib.rs index 2e00b9f..eabd0e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,7 @@ extern crate quick_xml; pub mod error; pub mod element; pub mod convert; +mod namespaces; #[cfg(test)] mod tests; diff --git a/src/namespaces.rs b/src/namespaces.rs new file mode 100644 index 0000000..590d9cb --- /dev/null +++ b/src/namespaces.rs @@ -0,0 +1,105 @@ +use std::collections::BTreeMap; +use std::cell::RefCell; +use std::rc::Rc; + + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct NamespaceSet { + parent: RefCell>>, + namespaces: BTreeMap, String>, +} + +impl Default for NamespaceSet { + fn default() -> Self { + NamespaceSet { + parent: RefCell::new(None), + namespaces: BTreeMap::new(), + } + } +} + +impl NamespaceSet { + pub fn declared_ns(&self) -> &BTreeMap, String> { + &self.namespaces + } + + pub 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) + }, + } + } + + pub 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), + }, + } + } + + pub fn set_parent(&self, parent: Rc) { + let mut parent_ns = self.parent.borrow_mut(); + let new_set = parent; + *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)) + } +} From 7297dbc5fd37ac9c50e4626f82f984a79758710e Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 02:48:28 +0200 Subject: [PATCH 7/9] namespaces: add some tests --- src/namespaces.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/namespaces.rs b/src/namespaces.rs index 590d9cb..7671062 100644 --- a/src/namespaces.rs +++ b/src/namespaces.rs @@ -103,3 +103,48 @@ impl From<(String, String)> for NamespaceSet { Self::from((Some(prefix), namespace)) } } + +#[cfg(test)] +mod tests { + use super::*; + use std::rc::Rc; + + #[test] + fn get_has() { + let namespaces = NamespaceSet::from("foo".to_owned()); + assert_eq!(namespaces.get(&None), Some("foo".to_owned())); + assert!(namespaces.has(&None, "foo")); + } + + #[test] + fn get_has_prefixed() { + let namespaces = NamespaceSet::from(("x".to_owned(), "bar".to_owned())); + assert_eq!(namespaces.get(&Some("x".to_owned())), Some("bar".to_owned())); + assert!(namespaces.has(&Some("x".to_owned()), "bar")); + } + + #[test] + fn get_has_recursive() { + let mut parent = NamespaceSet::from("foo".to_owned()); + for _ in 0..1000 { + let namespaces = NamespaceSet::default(); + namespaces.set_parent(Rc::new(parent)); + assert_eq!(namespaces.get(&None), Some("foo".to_owned())); + assert!(namespaces.has(&None, "foo")); + parent = namespaces; + } + } + + #[test] + fn get_has_prefixed_recursive() { + let mut parent = NamespaceSet::from(("x".to_owned(), "bar".to_owned())); + for _ in 0..1000 { + let namespaces = NamespaceSet::default(); + namespaces.set_parent(Rc::new(parent)); + assert_eq!(namespaces.get(&Some("x".to_owned())), Some("bar".to_owned())); + assert!(namespaces.has(&Some("x".to_owned()), "bar")); + parent = namespaces; + } + } + +} From b14155908115014e91a8e7f4968b518740484ea3 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 17:29:38 +0200 Subject: [PATCH 8/9] rename namespaces mod to namespace_set --- src/element.rs | 2 +- src/lib.rs | 2 +- src/{namespaces.rs => namespace_set.rs} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{namespaces.rs => namespace_set.rs} (100%) diff --git a/src/element.rs b/src/element.rs index 47e25d7..e0aebf8 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 namespaces::NamespaceSet; +use namespace_set::NamespaceSet; /// Escape XML text pub fn write_escaped(writer: &mut W, input: &str) -> Result<()> { diff --git a/src/lib.rs b/src/lib.rs index eabd0e7..b11fe06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,7 @@ extern crate quick_xml; pub mod error; pub mod element; pub mod convert; -mod namespaces; +mod namespace_set; #[cfg(test)] mod tests; diff --git a/src/namespaces.rs b/src/namespace_set.rs similarity index 100% rename from src/namespaces.rs rename to src/namespace_set.rs From 2398e4f6d1938bad3fe3dabe8aef20bc81c87aea Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 13 Aug 2017 17:29:48 +0200 Subject: [PATCH 9/9] Cargo.toml: add Astro to authors --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ebb09d1..bb7a765 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "minidom" version = "0.5.0" -authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel "] +authors = ["lumi ", "Emmanuel Gil Peyrot ", "Bastien Orivel ", "Astro "] description = "A small, simple DOM implementation on top of quick-xml" homepage = "https://gitlab.com/lumi/minidom-rs" repository = "https://gitlab.com/lumi/minidom-rs"