diff --git a/minidom-rs/src/element.rs b/minidom-rs/src/element.rs index a663c737f4dbd21e8760d5b4734fc17f7f0c3a12..19ff0b6dcfa61524adaa7abbe84eb018e69fced6 100644 --- a/minidom-rs/src/element.rs +++ b/minidom-rs/src/element.rs @@ -14,7 +14,8 @@ use crate::convert::IntoAttributeValue; use crate::error::{Error, Result}; -use crate::namespace_set::{NSChoice, NamespaceSet}; +use crate::namespaces::NSChoice; +use crate::prefixes::Prefixes; use crate::node::Node; use std::collections::{btree_map, BTreeMap}; @@ -83,9 +84,9 @@ pub fn escape(raw: &[u8]) -> Cow<[u8]> { #[derive(Clone, Eq, Debug)] /// A struct representing a DOM Element. pub struct Element { - prefix: Option, name: String, - namespaces: Rc, + namespace: String, + prefixes: Rc, attributes: BTreeMap, children: Vec, } @@ -121,17 +122,18 @@ impl PartialEq for Element { } impl Element { - fn new>( + fn new>( name: String, - prefix: Option, - namespaces: NS, + namespace: String, + prefixes: P, attributes: BTreeMap, children: Vec, ) -> Element { + // TODO split name and possible prefix. Element { - prefix, name, - namespaces: Rc::new(namespaces.into()), + namespace, + prefixes: Rc::new(prefixes.into()), attributes, children, } @@ -144,23 +146,31 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let elem = Element::builder("name") - /// .ns("namespace") + /// let elem = Element::builder("name", "namespace") /// .attr("name", "value") /// .append("inner") /// .build(); /// /// assert_eq!(elem.name(), "name"); - /// assert_eq!(elem.ns(), Some("namespace".to_owned())); + /// assert_eq!(elem.ns(), "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, NS: Into>(name: S, namespace: NS) -> ElementBuilder { let (prefix, name) = split_element_name(name).unwrap(); + let namespace: String = namespace.into(); + let prefixes: BTreeMap> = match prefix { + None => Default::default(), + Some(_) => { + let mut prefixes: BTreeMap> = BTreeMap::new(); + prefixes.insert(namespace.clone(), prefix); + prefixes + }, + }; ElementBuilder { - root: Element::new(name, prefix, None, BTreeMap::new(), Vec::new()), - namespaces: Default::default(), + root: Element::new(name, namespace, None, BTreeMap::new(), Vec::new()), + prefixes, } } @@ -171,47 +181,33 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let bare = Element::bare("name"); + /// let bare = Element::bare("name", "namespace"); /// /// assert_eq!(bare.name(), "name"); - /// assert_eq!(bare.ns(), None); + /// assert_eq!(bare.ns(), "namespace"); /// assert_eq!(bare.attr("name"), None); /// assert_eq!(bare.text(), ""); /// ``` - pub fn bare>(name: S) -> Element { + pub fn bare, NS: Into>(name: S, namespace: NS) -> Element { + // TODO split name and possible prefix. Element { - prefix: None, name: name.into(), - namespaces: Rc::new(NamespaceSet::default()), + namespace: namespace.into(), + prefixes: Rc::new(Prefixes::default()), attributes: BTreeMap::new(), children: Vec::new(), } } /// Returns a reference to the name of this element. + // TODO: rename local_name pub fn name(&self) -> &str { &self.name } - /// Returns a reference to the prefix of this element. - /// - /// # Examples - /// ```rust - /// use minidom::Element; - /// - /// let elem = Element::builder("prefix:name") - /// .build(); - /// - /// assert_eq!(elem.name(), "name"); - /// assert_eq!(elem.prefix(), Some("prefix")); - /// ``` - pub fn prefix(&self) -> Option<&str> { - self.prefix.as_ref().map(String::as_ref) - } - /// Returns a reference to the namespace of this element, if it has one, else `None`. - pub fn ns(&self) -> Option { - self.namespaces.get(&self.prefix) + pub fn ns(&self) -> String { + self.namespace.clone() } /// Returns a reference to the value of the given attribute, if it exists, else `None`. @@ -229,7 +225,7 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let elm: Element = "".parse().unwrap(); + /// let elm: Element = "".parse().unwrap(); /// /// let mut iter = elm.attrs(); /// @@ -273,7 +269,7 @@ impl Element { /// ```rust /// use minidom::{Element, NSChoice}; /// - /// let elem = Element::builder("name").ns("namespace").build(); + /// let elem = Element::builder("name", "namespace").build(); /// /// assert_eq!(elem.is("name", "namespace"), true); /// assert_eq!(elem.is("name", "wrong"), false); @@ -284,13 +280,9 @@ impl Element { /// assert_eq!(elem.is("name", NSChoice::OneOf("foo")), false); /// assert_eq!(elem.is("name", NSChoice::AnyOf(&["foo", "namespace"])), true); /// assert_eq!(elem.is("name", NSChoice::Any), true); - /// - /// let elem2 = Element::builder("name").build(); - /// - /// assert_eq!(elem2.is("name", NSChoice::Any), true); /// ``` pub fn is<'a, N: AsRef, NS: Into>>(&self, name: N, namespace: NS) -> bool { - self.name == name.as_ref() && self.has_ns(namespace) + self.name == name.as_ref() && namespace.into().compare(self.namespace.as_ref()) } /// Returns whether the element has the given namespace. @@ -300,7 +292,7 @@ impl Element { /// ```rust /// use minidom::{Element, NSChoice}; /// - /// let elem = Element::builder("name").ns("namespace").build(); + /// let elem = Element::builder("name", "namespace").build(); /// /// assert_eq!(elem.has_ns("namespace"), true); /// assert_eq!(elem.has_ns("wrong"), false); @@ -309,24 +301,21 @@ impl Element { /// assert_eq!(elem.has_ns(NSChoice::OneOf("foo")), false); /// assert_eq!(elem.has_ns(NSChoice::AnyOf(&["foo", "namespace"])), true); /// assert_eq!(elem.has_ns(NSChoice::Any), true); - /// - /// let elem2 = Element::builder("name").build(); - /// - /// assert_eq!(elem2.has_ns(NSChoice::Any), true); /// ``` pub fn has_ns<'a, NS: Into>>(&self, namespace: NS) -> bool { - self.namespaces.has(&self.prefix, namespace) + namespace.into().compare(self.namespace.as_ref()) } /// Parse a document from an `EventReader`. pub fn from_reader(reader: &mut EventReader) -> Result { let mut buf = Vec::new(); + let mut prefixes = BTreeMap::new(); let root: Element = loop { let e = reader.read_event(&mut buf)?; match e { Event::Empty(ref e) | Event::Start(ref e) => { - break build_element(reader, e)?; + break build_element(reader, e, &mut prefixes)?; } Event::Eof => { return Err(Error::EndOfDocument); @@ -344,22 +333,27 @@ impl Element { }; let mut stack = vec![root]; + let mut prefix_stack = vec![prefixes]; loop { match reader.read_event(&mut buf)? { Event::Empty(ref e) => { - let elem = build_element(reader, e)?; + let mut prefixes = prefix_stack.last().unwrap().clone(); + let elem = build_element(reader, e, &mut prefixes)?; // Since there is no Event::End after, directly append it to the current node stack.last_mut().unwrap().append_child(elem); } Event::Start(ref e) => { - let elem = build_element(reader, e)?; + let mut prefixes = prefix_stack.last().unwrap().clone(); + let elem = build_element(reader, e, &mut prefixes)?; stack.push(elem); + prefix_stack.push(prefixes); } Event::End(ref e) => { if stack.len() <= 1 { break; } + prefix_stack.pop().unwrap(); let elem = stack.pop().unwrap(); if let Some(to) = stack.last_mut() { // TODO: check whether this is correct, we are comparing &[u8]s, not &strs @@ -368,13 +362,13 @@ impl Element { let possible_prefix = split_iter.next().unwrap(); // Can't be empty. match split_iter.next() { Some(name) => { - match elem.prefix() { - Some(prefix) => { + match elem.prefixes.get(&elem.namespace) { + Some(Some(prefix)) => { if possible_prefix != prefix.as_bytes() { return Err(Error::InvalidElementClosed); } } - None => { + _ => { return Err(Error::InvalidElementClosed); } } @@ -383,8 +377,9 @@ impl Element { } } None => { - if elem.prefix().is_some() { - return Err(Error::InvalidElementClosed); + match elem.prefixes.get(&elem.namespace) { + Some(Some(_)) => return Err(Error::InvalidElementClosed), + _ => (), } if possible_prefix != elem.name().as_bytes() { return Err(Error::InvalidElementClosed); @@ -430,32 +425,93 @@ impl Element { /// Output the document to quick-xml `Writer` pub fn to_writer(&self, writer: &mut EventWriter) -> Result<()> { - self.write_to_inner(writer) + self.write_to_inner(writer, &mut BTreeMap::new()) } /// Output the document to quick-xml `Writer` pub fn to_writer_decl(&self, writer: &mut EventWriter) -> Result<()> { writer.write_event(Event::Decl(BytesDecl::new(b"1.0", Some(b"utf-8"), None)))?; - self.write_to_inner(writer) + self.write_to_inner(writer, &mut BTreeMap::new()) } /// Like `write_to()` but without the `` prelude - pub fn write_to_inner(&self, writer: &mut EventWriter) -> Result<()> { - let name = match self.prefix { - None => Cow::Borrowed(&self.name), - Some(ref prefix) => Cow::Owned(format!("{}:{}", prefix, self.name)), + pub fn write_to_inner(&self, writer: &mut EventWriter, all_prefixes: &mut BTreeMap, String>) -> Result<()> { + let local_prefixes = self.prefixes.declared_prefixes(); + + // Element namespace + // If the element prefix hasn't been set yet via a custom prefix, add it. + let mut existing_self_prefix: Option> = None; + if let Some(prefix) = local_prefixes.get(&self.namespace) { + existing_self_prefix = Some(prefix.clone()); + } else { + for (prefix, ns) in all_prefixes.iter() { + if ns == &self.namespace { + existing_self_prefix = Some(prefix.clone()); + } + } + } + + let mut all_keys = all_prefixes.keys().cloned(); + let mut local_keys = local_prefixes.values().cloned(); + let self_prefix: (Option, bool) = match existing_self_prefix { + // No prefix exists already for our namespace + None => { + if local_keys.find(|p| p == &None).is_none() { + // Use the None prefix if available + (None, true) + } else { + // Otherwise generate one. Check if it isn't already used, if so increase the + // number until we find a suitable one. + let mut prefix_n = 0u8; + while let Some(_) = all_keys.find(|p| p == &Some(format!("ns{}", prefix_n))) { + prefix_n += 1; + } + (Some(format!("ns{}", prefix_n)), true) + } + }, + // Some prefix has already been declared (or is going to be) for our namespace. We + // don't need to declare a new one. We do however need to remember which one to use in + // the tag name. + Some(prefix) => (prefix, false), }; + let name = match self_prefix { + (Some(ref prefix), _) => Cow::Owned(format!("{}:{}", prefix, self.name)), + _ => Cow::Borrowed(&self.name), + }; let mut start = BytesStart::borrowed(name.as_bytes(), name.len()); - for (prefix, ns) in self.namespaces.declared_ns() { - match *prefix { - None => start.push_attribute(("xmlns", ns.as_ref())), - Some(ref prefix) => { - let key = format!("xmlns:{}", prefix); - start.push_attribute((key.as_bytes(), ns.as_bytes())) - } + + // Write self prefix if necessary + match self_prefix { + (Some(ref p), true) => { + let key = format!("xmlns:{}", p); + start.push_attribute((key.as_bytes(), self.namespace.as_bytes())); + all_prefixes.insert(self_prefix.0, self.namespace.clone()); + } + (None, true) => { + let key = format!("xmlns"); + start.push_attribute((key.as_bytes(), self.namespace.as_bytes())); + all_prefixes.insert(self_prefix.0, self.namespace.clone()); + }, + _ => (), + }; + + // Custom prefixes/namespace sets + for (ns, prefix) in local_prefixes { + match all_prefixes.get(prefix) { + p @ Some(_) if p == prefix.as_ref() => (), + _ => { + let key = match prefix { + None => String::from("xmlns"), + Some(p) => format!("xmlns:{}", p), + }; + + start.push_attribute((key.as_bytes(), ns.as_ref())); + all_prefixes.insert(prefix.clone(), ns.clone()); + }, } } + for (key, value) in &self.attributes { start.push_attribute((key.as_bytes(), escape(value.as_bytes()).as_ref())); } @@ -468,7 +524,7 @@ impl Element { writer.write_event(Event::Start(start))?; for child in &self.children { - child.write_to_inner(writer)?; + child.write_to_inner(writer, all_prefixes)?; } writer.write_event(Event::End(BytesEnd::borrowed(name.as_bytes())))?; @@ -482,7 +538,7 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let elem: Element = "abc".parse().unwrap(); + /// let elem: Element = "abc".parse().unwrap(); /// /// let mut iter = elem.nodes(); /// @@ -511,7 +567,7 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let elem: Element = "hellothisisignored".parse().unwrap(); + /// let elem: Element = "hellothisisignored".parse().unwrap(); /// /// let mut iter = elem.children(); /// assert_eq!(iter.next().unwrap().name(), "child1"); @@ -541,7 +597,7 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let elem: Element = "hello world!".parse().unwrap(); + /// let elem: Element = "hello world!".parse().unwrap(); /// /// let mut iter = elem.texts(); /// assert_eq!(iter.next().unwrap(), "hello"); @@ -570,11 +626,11 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let mut elem = Element::bare("root"); + /// let mut elem = Element::bare("root", "ns1"); /// /// assert_eq!(elem.children().count(), 0); /// - /// elem.append_child(Element::bare("child")); + /// elem.append_child(Element::bare("child", "ns1")); /// /// { /// let mut iter = elem.children(); @@ -582,13 +638,11 @@ impl Element { /// assert_eq!(iter.next(), None); /// } /// - /// let child = elem.append_child(Element::bare("new")); + /// let child = elem.append_child(Element::bare("new", "ns1")); /// /// assert_eq!(child.name(), "new"); /// ``` pub fn append_child(&mut self, child: Element) -> &mut Element { - child.namespaces.set_parent(Rc::clone(&self.namespaces)); - self.children.push(Node::Element(child)); if let Node::Element(ref mut cld) = *self.children.last_mut().unwrap() { cld @@ -604,7 +658,7 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let mut elem = Element::bare("node"); + /// let mut elem = Element::bare("node", "ns1"); /// /// assert_eq!(elem.text(), ""); /// @@ -623,7 +677,7 @@ impl Element { /// ```rust /// use minidom::{Element, Node}; /// - /// let mut elem = Element::bare("node"); + /// let mut elem = Element::bare("node", "ns1"); /// /// elem.append_node(Node::Text("hello".to_owned())); /// @@ -640,7 +694,7 @@ impl Element { /// ```rust /// use minidom::Element; /// - /// let elem: Element = "hello, world!".parse().unwrap(); + /// let elem: Element = "hello, world!".parse().unwrap(); /// /// assert_eq!(elem.text(), "hello, world!"); /// ``` @@ -656,7 +710,7 @@ impl Element { /// ```rust /// use minidom::{Element, NSChoice}; /// - /// let elem: Element = r#""#.parse().unwrap(); + /// let elem: Element = r#""#.parse().unwrap(); /// assert!(elem.get_child("a", "ns").unwrap().is("a", "ns")); /// assert!(elem.get_child("a", "other_ns").unwrap().is("a", "other_ns")); /// assert!(elem.get_child("b", "ns").unwrap().is("b", "ns")); @@ -763,8 +817,10 @@ fn split_element_name>(s: S) -> Result<(Option, String)> { } } -fn build_element(reader: &EventReader, event: &BytesStart) -> Result { - let mut namespaces = BTreeMap::new(); +fn build_element(reader: &EventReader, event: &BytesStart, prefixes: &mut BTreeMap>) -> Result { + let (prefix, name) = split_element_name(str::from_utf8(event.name())?)?; + let mut local_prefixes = BTreeMap::new(); + let attributes = event .attributes() .map(|o| { @@ -775,19 +831,33 @@ fn build_element(reader: &EventReader, event: &BytesStart) -> Res }) .filter(|o| match *o { Ok((ref key, ref value)) if key == "xmlns" => { - namespaces.insert(None, value.to_owned()); + local_prefixes.insert(value.clone(), None); + prefixes.insert(value.clone(), None); false } Ok((ref key, ref value)) if key.starts_with("xmlns:") => { - namespaces.insert(Some(key[6..].to_owned()), value.to_owned()); + local_prefixes.insert(value.to_owned(), Some(key[6..].to_owned())); + prefixes.insert(value.to_owned(), Some(key[6..].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()); + let namespace: String = { + let mut tmp: Option = None; + + for (k, v) in local_prefixes.iter().chain(prefixes.iter()) { + if v == &prefix { + tmp = Some(k.clone()); + break; + } + } + + tmp.ok_or(Error::MissingNamespace)? + }; + + let element = Element::new(name, namespace, local_prefixes, attributes, Vec::new()); Ok(element) } @@ -898,14 +968,13 @@ impl<'a> Iterator for AttrsMut<'a> { /// A builder for `Element`s. pub struct ElementBuilder { root: Element, - namespaces: BTreeMap, String>, + prefixes: BTreeMap>, } impl ElementBuilder { - /// Sets the namespace. - pub fn ns>(mut self, namespace: S) -> ElementBuilder { - self.namespaces - .insert(self.root.prefix.clone(), namespace.into()); + /// Sets a custom prefix. + pub fn prefix>(mut self, prefix: Option, namespace: S) -> ElementBuilder { + self.prefixes.insert(namespace.into(), prefix); self } @@ -940,13 +1009,7 @@ impl ElementBuilder { pub fn build(self) -> Element { 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(Rc::clone(&element.namespaces)); - } - } + element.prefixes = Rc::new(Prefixes::from(self.prefixes)); element } } @@ -961,49 +1024,49 @@ mod tests { let elem = Element::new( "name".to_owned(), - None, - Some("namespace".to_owned()), + "namespace".to_owned(), + (None, "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".to_owned())); + assert_eq!(elem.ns(), "namespace".to_owned()); assert_eq!(elem.attr("name"), Some("value")); assert_eq!(elem.attr("inexistent"), None); } #[test] fn test_from_reader_simple() { - let xml = ""; + let xml = ""; let mut reader = EventReader::from_str(xml); let elem = Element::from_reader(&mut reader); - let elem2 = Element::builder("foo").build(); + let elem2 = Element::builder("foo", "ns1").build(); assert_eq!(elem.unwrap(), elem2); } #[test] fn test_from_reader_nested() { - let xml = ""; + let xml = ""; let mut reader = EventReader::from_str(xml); let elem = Element::from_reader(&mut reader); - let nested = Element::builder("bar").attr("baz", "qxx").build(); - let elem2 = Element::builder("foo").append(nested).build(); + let nested = Element::builder("bar", "ns1").attr("baz", "qxx").build(); + let elem2 = Element::builder("foo", "ns1").append(nested).build(); assert_eq!(elem.unwrap(), elem2); } #[test] fn test_from_reader_with_prefix() { - let xml = ""; + let xml = ""; let mut reader = EventReader::from_str(xml); let elem = Element::from_reader(&mut reader); - let nested = Element::builder("prefix:bar").attr("baz", "qxx").build(); - let elem2 = Element::builder("foo").append(nested).build(); + let nested = Element::builder("prefix:bar", "ns1").attr("baz", "qxx").build(); + let elem2 = Element::builder("foo", "ns1").append(nested).build(); assert_eq!(elem.unwrap(), elem2); } @@ -1022,7 +1085,7 @@ mod tests { #[test] fn does_not_unescape_cdata() { - let xml = "]]>"; + let xml = "]]>"; let mut reader = EventReader::from_str(xml); let elem = Element::from_reader(&mut reader).unwrap(); assert_eq!(elem.text(), "'>blah"); @@ -1030,7 +1093,7 @@ mod tests { #[test] fn test_compare_all_ns() { - let xml = ""; + let xml = ""; let mut reader = EventReader::from_str(xml); let elem = Element::from_reader(&mut reader).unwrap(); diff --git a/minidom-rs/src/error.rs b/minidom-rs/src/error.rs index 666e3aa70f3cd9e2dcdc59bf2b5ae7b546291068..acffc5f0874113d30edfef8d8ff5fe8bcfb210be 100644 --- a/minidom-rs/src/error.rs +++ b/minidom-rs/src/error.rs @@ -35,6 +35,9 @@ pub enum Error { /// An error which is returned when an elemet's name contains more than one colon InvalidElement, + /// An error which is returned when an element doesn't contain a namespace + MissingNamespace, + /// An error which is returned when a comment is to be parsed by minidom NoComments, } @@ -48,6 +51,7 @@ impl StdError for Error { Error::EndOfDocument => None, Error::InvalidElementClosed => None, Error::InvalidElement => None, + Error::MissingNamespace => None, Error::NoComments => None, } } @@ -66,6 +70,10 @@ impl std::fmt::Display for Error { write!(fmt, "the XML is invalid, an element was wrongly closed") } Error::InvalidElement => write!(fmt, "the XML element is invalid"), + Error::MissingNamespace => write!( + fmt, + "the XML element is missing a namespace", + ), Error::NoComments => write!( fmt, "a comment has been found even though comments are forbidden" diff --git a/minidom-rs/src/lib.rs b/minidom-rs/src/lib.rs index 1d996b01fc5e30811fe0ea7c84ea2099afb35904..bc47a2f6bf4e7b07eb2a1e1ab6e0e1265f2be506 100644 --- a/minidom-rs/src/lib.rs +++ b/minidom-rs/src/lib.rs @@ -79,7 +79,8 @@ pub use quick_xml; pub mod convert; pub mod element; pub mod error; -mod namespace_set; +mod namespaces; +mod prefixes; pub mod node; #[cfg(test)] @@ -88,5 +89,5 @@ mod tests; pub use convert::IntoAttributeValue; pub use element::{Children, ChildrenMut, Element, ElementBuilder}; pub use error::{Error, Result}; -pub use namespace_set::NSChoice; +pub use namespaces::NSChoice; pub use node::Node; diff --git a/minidom-rs/src/namespace_set.rs b/minidom-rs/src/namespace_set.rs deleted file mode 100644 index 986a92d03748bde83f874ab8484d4047290bc7b7..0000000000000000000000000000000000000000 --- a/minidom-rs/src/namespace_set.rs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2020 Emmanuel Gil Peyrot -// Copyright (c) 2020 Astro -// Copyright (c) 2020 Maxime “pep” Buquet -// Copyright (c) 2020 Xidorn Quan -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use std::cell::RefCell; -use std::collections::BTreeMap; -use std::fmt; -use std::rc::Rc; - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -/// Use to compare namespaces -pub enum NSChoice<'a> { - /// The element's namespace must match the specified namespace - OneOf(&'a str), - /// The element's namespace must be in the specified vector - AnyOf(&'a [&'a str]), - /// The element can have any namespace, or no namespace - Any, -} - -impl<'a> From<&'a str> for NSChoice<'a> { - fn from(ns: &'a str) -> NSChoice<'a> { - NSChoice::OneOf(ns) - } -} - -impl<'a> NSChoice<'a> { - fn compare(&self, ns: Option<&str>) -> bool { - match (ns, &self) { - (None, NSChoice::Any) => true, - (None, NSChoice::OneOf(_)) | (None, NSChoice::AnyOf(_)) => false, - (Some(_), NSChoice::Any) => true, - (Some(ns), NSChoice::OneOf(wanted_ns)) => &ns == wanted_ns, - (Some(ns), NSChoice::AnyOf(wanted_nss)) => wanted_nss.iter().any(|w| &ns == w), - } - } -} - -#[derive(Clone, 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 fmt::Debug for NamespaceSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "NamespaceSet(")?; - for (prefix, namespace) in &self.namespaces { - write!( - f, - "xmlns{}={:?}, ", - match prefix { - None => String::new(), - Some(prefix) => format!(":{}", prefix), - }, - namespace - )?; - } - write!(f, "parent: {:?})", *self.parent.borrow()) - } -} - -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<'a, NS: Into>>(&self, prefix: &Option, wanted_ns: NS) -> bool { - match self.namespaces.get(prefix) { - Some(ns) => wanted_ns.into().compare(Some(ns)), - None => match *self.parent.borrow() { - None => wanted_ns.into().compare(None), - 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, - } - } -} - -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, - } - } -} - -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, - } - } -} - -impl From<(String, String)> for NamespaceSet { - fn from(prefix_namespace: (String, String)) -> Self { - let (prefix, namespace) = prefix_namespace; - Self::from((Some(prefix), namespace)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[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; - } - } - - #[test] - fn debug_looks_correct() { - let parent = NamespaceSet::from("http://www.w3.org/2000/svg".to_owned()); - let namespaces = NamespaceSet::from(( - "xhtml".to_owned(), - "http://www.w3.org/1999/xhtml".to_owned(), - )); - namespaces.set_parent(Rc::new(parent)); - assert_eq!(format!("{:?}", namespaces), "NamespaceSet(xmlns:xhtml=\"http://www.w3.org/1999/xhtml\", parent: Some(NamespaceSet(xmlns=\"http://www.w3.org/2000/svg\", parent: None)))"); - } -} diff --git a/minidom-rs/src/namespaces.rs b/minidom-rs/src/namespaces.rs new file mode 100644 index 0000000000000000000000000000000000000000..7d81744065b8e5b7af19779e94c9b1c18486c6b9 --- /dev/null +++ b/minidom-rs/src/namespaces.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2020 Emmanuel Gil Peyrot +// Copyright (c) 2020 Astro +// Copyright (c) 2020 Maxime “pep” Buquet +// Copyright (c) 2020 Xidorn Quan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +/// Use to compare namespaces +pub enum NSChoice<'a> { + /// The element must have no namespace + None, + /// The element's namespace must match the specified namespace + OneOf(&'a str), + /// The element's namespace must be in the specified vector + AnyOf(&'a [&'a str]), + /// The element can have any namespace, or no namespace + Any, +} + +impl<'a> From<&'a str> for NSChoice<'a> { + fn from(ns: &'a str) -> NSChoice<'a> { + NSChoice::OneOf(ns) + } +} + +impl<'a> NSChoice<'a> { + pub(crate) fn compare(&self, ns: &str) -> bool { + match (ns, &self) { + (_, NSChoice::None) => false, + (_, NSChoice::Any) => true, + (ns, NSChoice::OneOf(wanted_ns)) => &ns == wanted_ns, + (ns, NSChoice::AnyOf(wanted_nss)) => wanted_nss.iter().any(|w| &ns == w), + } + } +} diff --git a/minidom-rs/src/node.rs b/minidom-rs/src/node.rs index 501b22b5c2dd938cabbc428783dbc878d234e03f..0a0615ca803df4db431b1e28158d9e954d7b6c19 100644 --- a/minidom-rs/src/node.rs +++ b/minidom-rs/src/node.rs @@ -12,6 +12,7 @@ use crate::element::{Element, ElementBuilder}; use crate::error::Result; use std::io::Write; +use std::collections::BTreeMap; use quick_xml::events::{BytesText, Event}; use quick_xml::Writer as EventWriter; @@ -34,7 +35,7 @@ impl Node { /// ```rust /// use minidom::Node; /// - /// let elm = Node::Element("".parse().unwrap()); + /// let elm = Node::Element("".parse().unwrap()); /// let txt = Node::Text("meow".to_owned()); /// /// assert_eq!(elm.as_element().unwrap().name(), "meow"); @@ -55,7 +56,7 @@ impl Node { /// ```rust /// use minidom::Node; /// - /// let mut elm = Node::Element("".parse().unwrap()); + /// let mut elm = Node::Element("".parse().unwrap()); /// let mut txt = Node::Text("meow".to_owned()); /// /// assert_eq!(elm.as_element_mut().unwrap().name(), "meow"); @@ -76,7 +77,7 @@ impl Node { /// ```rust /// use minidom::Node; /// - /// let elm = Node::Element("".parse().unwrap()); + /// let elm = Node::Element("".parse().unwrap()); /// let txt = Node::Text("meow".to_owned()); /// /// assert_eq!(elm.into_element().unwrap().name(), "meow"); @@ -97,7 +98,7 @@ impl Node { /// ```rust /// use minidom::Node; /// - /// let elm = Node::Element("".parse().unwrap()); + /// let elm = Node::Element("".parse().unwrap()); /// let txt = Node::Text("meow".to_owned()); /// /// assert_eq!(elm.as_text(), None); @@ -118,7 +119,7 @@ impl Node { /// ```rust /// use minidom::Node; /// - /// let mut elm = Node::Element("".parse().unwrap()); + /// let mut elm = Node::Element("".parse().unwrap()); /// let mut txt = Node::Text("meow".to_owned()); /// /// assert_eq!(elm.as_text_mut(), None); @@ -145,7 +146,7 @@ impl Node { /// ```rust /// use minidom::Node; /// - /// let elm = Node::Element("".parse().unwrap()); + /// let elm = Node::Element("".parse().unwrap()); /// let txt = Node::Text("meow".to_owned()); /// /// assert_eq!(elm.into_text(), None); @@ -159,9 +160,9 @@ impl Node { } #[doc(hidden)] - pub(crate) fn write_to_inner(&self, writer: &mut EventWriter) -> Result<()> { + pub(crate) fn write_to_inner(&self, writer: &mut EventWriter, prefixes: &mut BTreeMap, String>) -> Result<()> { match *self { - Node::Element(ref elmt) => elmt.write_to_inner(writer)?, + Node::Element(ref elmt) => elmt.write_to_inner(writer, prefixes)?, Node::Text(ref s) => { writer.write_event(Event::Text(BytesText::from_plain_str(s)))?; } diff --git a/minidom-rs/src/parser.rs b/minidom-rs/src/parser.rs new file mode 100644 index 0000000000000000000000000000000000000000..49896c3ade75562144ac1e3639a4aa7b80aa7144 --- /dev/null +++ b/minidom-rs/src/parser.rs @@ -0,0 +1,184 @@ +// Copyright (c) 2020 Maxime “pep” Buquet +// Copyright (c) 2020 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//! Provides a `Parser` type, which takes bytes and returns Elements. It also keeps a hold of +//! ascendant elements to be able to handle namespaces properly. + +use crate::element::Element; +use crate::error::{Error, ParserError, Result}; + +use bytes::BytesMut; +use quick_xml::Reader as EventReader; +use std::cell::RefCell; +use std::str; + +/// Parser +#[derive(Debug)] +pub struct Parser { + buffer: RefCell, + state: ParserState, +} + +/// Describes the state of the parser. +/// +/// This parser will only accept one-level documents. The root element is kept for convenience, to +/// be able to pass namespaces down to children who are themselves children. +#[derive(Debug)] +pub enum ParserState { + /// Not enough data has been processed to find the first element. + Empty, + + /// The normal state. the root element has been identified and children are processed. + Root { + /// Root element. Kept for future reference + root: Element, + + /// Child element + child: Option, + + /// XXX: Weird flag to say if we've already sent what we could send or if there's more to + /// send. This Variant needs to be changed. + sent: bool, + }, + + /// Something was passed in the buffer that made the parser get into an error state. + Error, + + /// The root element has been closed. No feed-ing can happen past this point. + Closed, +} + +/// Result of polling the parser +#[derive(Debug)] +pub enum ParserResult { + /// Buffer is not empty but needs more data + Partial, + + /// An Element has been generated from the buffer. + Single(Element), +} + +/* +/// Split and parse it. +fn split_stream_stream_stream_features(string: String) -> (Element, Element) { + let mut stuff = string.splitn(2, '>'); + let stream_opening_str = stuff.next().unwrap().to_string() + "/>"; + let rest = stuff.next().unwrap().to_string(); + let stream_opening: Element = stream_opening_str.parse().unwrap(); + let rest: Element = rest.parse().unwrap(); + println!("opening: {}", String::from(&stream_opening)); + println!("features: {}", String::from(&rest)); + (stream_opening, rest) +} +*/ + +fn maybe_split_prolog(string: &str) -> &str { + if string.starts_with("'); + stuff.next(); + stuff.next().unwrap() + } else { + string + } +} + +impl Parser { + /// Creates a new Parser + pub fn new() -> Parser { + Parser { + buffer: RefCell::new(BytesMut::new()), + state: ParserState::Empty, + } + } + + /// Feed bytes to the parser. + pub fn feed(&mut self, bytes: BytesMut) -> Result<()> { + self.buffer.borrow_mut().unsplit(bytes); + let state = match self.state { + ParserState::Empty => { + // TODO: Try splitting xml prolog and stream header + let foo = self.buffer.borrow(); + let header = maybe_split_prolog(str::from_utf8(foo.as_ref())?); + println!("FOO: header: {:?}", header); + let mut reader = EventReader::from_str(header); + let root = Element::from_reader(&mut reader); + match root { + Ok(root) => { + println!("FOO: elem: {:?}", root); + ParserState::Root { + root, + child: None, + sent: false, + } + } + Err(e) => { + println!("FOO: err: {:?}", e); + ParserState::Empty + } + } + } + ParserState::Closed => return Err(Error::ParserError(ParserError::Closed)), + _ => ParserState::Empty, + }; + + self.state = state; + Ok(()) + } + + /// Returns Elements to the application. + pub fn poll(&mut self) -> Result> { + match &self.state { + ParserState::Empty if self.buffer.borrow().len() != 0 => { + Ok(Some(ParserResult::Partial)) + } + ParserState::Empty | ParserState::Closed | ParserState::Error => Ok(None), + ParserState::Root { + root, child: None, .. + } => Ok(Some(ParserResult::Single(root.clone()))), + ParserState::Root { + child: Some(child), .. + } => Ok(Some(ParserResult::Single(child.clone()))), + } + } + + /// Resets the parser + pub fn reset(&mut self) { + *self = Parser::new(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bytes::{BufMut, BytesMut}; + + #[test] + fn test_prolog() { + let mut parser = Parser::new(); + let mut buf = BytesMut::new(); + buf.put(&b""[..]); + buf.put(&b""[..]); + match parser.feed(buf) { + Ok(_) => (), + _ => panic!(), + } + + let elem = Element::builder("stream:stream", "http://etherx.jabber.org/streams") + .prefix_ns(None, "jabber:client") + .attr("xml:lang", "en") + .attr("version", "1.0") + .attr("to", "foo.bar") + .build(); + + println!("BAR: elem: {:?}", elem); + + match parser.poll() { + Ok(Some(ParserResult::Single(e))) => assert_eq!(e, elem), + _ => panic!(), + } + } +} diff --git a/minidom-rs/src/prefixes.rs b/minidom-rs/src/prefixes.rs new file mode 100644 index 0000000000000000000000000000000000000000..c0508b9eb0edf4de2be8985d6bd2eb2cb26448ac --- /dev/null +++ b/minidom-rs/src/prefixes.rs @@ -0,0 +1,96 @@ +// Copyright (c) 2020 Emmanuel Gil Peyrot +// Copyright (c) 2020 Astro +// Copyright (c) 2020 Maxime “pep” Buquet +// Copyright (c) 2020 Xidorn Quan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::collections::BTreeMap; +use std::fmt; + +#[derive(Clone, PartialEq, Eq)] +pub struct Prefixes { + prefixes: BTreeMap>, +} + +impl Default for Prefixes { + fn default() -> Self { + Prefixes { + prefixes: BTreeMap::new(), + } + } +} + +impl fmt::Debug for Prefixes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Prefixes(")?; + for (namespace, prefix) in &self.prefixes { + write!( + f, + "xmlns{}={:?} ", + match prefix { + None => String::new(), + Some(prefix) => format!(":{}", prefix), + }, + namespace + )?; + } + write!(f, ")") + } +} + +impl Prefixes { + pub fn declared_prefixes(&self) -> &BTreeMap> { + &self.prefixes + } + + pub fn get(&self, namespace: &String) -> Option> { + match self.prefixes.get(namespace) { + Some(ns) => Some(ns.clone()), + None => None, + } + } +} + +impl From>> for Prefixes { + fn from(prefixes: BTreeMap>) -> Self { + Prefixes { prefixes } + } +} + +impl From> for Prefixes { + fn from(namespace: Option) -> Self { + match namespace { + None => Self::default(), + Some(namespace) => Self::from(namespace), + } + } +} + +impl From for Prefixes { + fn from(namespace: String) -> Self { + let mut prefixes = BTreeMap::new(); + prefixes.insert(namespace, None); + + Prefixes { prefixes } + } +} + +impl From<(Option, String)> for Prefixes { + fn from(prefix_namespace: (Option, String)) -> Self { + let (prefix, namespace) = prefix_namespace; + let mut prefixes = BTreeMap::new(); + prefixes.insert(namespace, prefix); + + Prefixes { prefixes } + } +} + +impl From<(String, String)> for Prefixes { + fn from(prefix_namespace: (String, String)) -> Self { + let (prefix, namespace) = prefix_namespace; + Self::from((Some(prefix), namespace)) + } +} diff --git a/minidom-rs/src/tests.rs b/minidom-rs/src/tests.rs index 79cdff937536d5ca07b2f3eef565413b524c7037..3d34fe3d0b48a24aab3c2c6ef48ee6863bbb659a 100644 --- a/minidom-rs/src/tests.rs +++ b/minidom-rs/src/tests.rs @@ -18,16 +18,14 @@ use quick_xml::Reader; const TEST_STRING: &'static str = r#"meownya"#; fn build_test_tree() -> Element { - let mut root = Element::builder("root") - .ns("root_ns") + let mut root = Element::builder("root", "root_ns") .attr("xml:lang", "en") .attr("a", "b") .build(); root.append_text_node("meow"); - let child = Element::builder("child").attr("c", "d").build(); + let child = Element::builder("child", "root_ns").attr("c", "d").build(); root.append_child(child); - let other_child = Element::builder("child") - .ns("child_ns") + let other_child = Element::builder("child", "child_ns") .attr("d", "e") .attr("xml:lang", "fr") .build(); @@ -45,6 +43,80 @@ fn reader_works() { ); } +#[test] +fn reader_deduplicate_prefixes() { + // The reader shouldn't complain that "child" doesn't have a namespace. It should reuse the + // parent ns with the same prefix. + let _: Element = r#""#.parse().unwrap(); + let _: Element = r#""#.parse().unwrap(); + let _: Element = r#""#.parse().unwrap(); + + match r#""#.parse::() { + Err(Error::MissingNamespace) => (), + Err(err) => panic!("No or wrong error: {:?}", err), + Ok(elem) => panic!("Got Element: {}; was expecting Error::MissingNamespace", String::from(&elem)), + } +} + +#[test] +fn reader_no_deduplicate_sibling_prefixes() { + // The reader shouldn't reuse the sibling's prefixes + match r#""#.parse::() { + Err(Error::MissingNamespace) => (), + Err(err) => panic!("No or wrong error: {:?}", err), + Ok(elem) => panic!("Got Element:\n{:?}\n{}\n; was expecting Error::MissingNamespace", elem, String::from(&elem)), + } +} + +#[test] +fn test_real_data() { + let correction = Element::builder("replace", "urn:xmpp:message-correct:0").build(); + let body = Element::builder("body", "jabber:client").build(); + let message = Element::builder("message", "jabber:client") + .append(body) + .append(correction) + .build(); + let stream = Element::builder("stream", "http://etherx.jabber.org/streams") + .prefix(Some(String::from("stream")), "http://etherx.jabber.org/streams") + .prefix(None, "jabber:client") + .append(message) + .build(); + println!("{}", String::from(&stream)); + + let jid = Element::builder("jid", "urn:xmpp:presence:0").build(); + let nick = Element::builder("nick", "urn:xmpp:presence:0").build(); + let mix = Element::builder("mix", "urn:xmpp:presence:0") + .append(jid) + .append(nick) + .build(); + let show = Element::builder("show", "jabber:client").build(); + let status = Element::builder("status", "jabber:client").build(); + let presence = Element::builder("presence", "jabber:client") + .append(show) + .append(status) + .append(mix) + .build(); + let item = Element::builder("item", "http://jabber.org/protocol/pubsub") + .append(presence) + .build(); + let items = Element::builder("items", "http://jabber.org/protocol/pubsub") + .append(item) + .build(); + let pubsub = Element::builder("pubsub", "http://jabber.org/protocol/pubsub") + .append(items) + .build(); + let iq = Element::builder("iq", "jabber:client") + .append(pubsub) + .build(); + let stream = Element::builder("stream", "http://etherx.jabber.org/streams") + .prefix(Some(String::from("stream")), "http://etherx.jabber.org/streams") + .prefix(None, "jabber:client") + .append(iq) + .build(); + + println!("{}", String::from(&stream)); +} + #[test] fn writer_works() { let root = build_test_tree(); @@ -66,39 +138,115 @@ fn writer_with_decl_works() { assert_eq!(String::from_utf8(writer).unwrap(), result); } +#[test] +fn writer_with_prefix() { + let root = Element::builder("root", "ns1") + .prefix(Some(String::from("p1")), "ns1") + .prefix(None, "ns2") + .build(); + assert_eq!(String::from(&root), + r#""#, + ); +} + +#[test] +fn writer_no_prefix_namespace() { + let root = Element::builder("root", "ns1").build(); + // TODO: Note that this isn't exactly equal to a None prefix. it's just that the None prefix is + // the most obvious when it's not already used. Maybe fix tests so that it only checks that the + // prefix used equals the one declared for the namespace. + assert_eq!(String::from(&root), r#""#); +} + +#[test] +fn writer_no_prefix_namespace_child() { + let child = Element::builder("child", "ns1").build(); + let root = Element::builder("root", "ns1") + .append(child) + .build(); + // TODO: Same remark as `writer_no_prefix_namespace`. + assert_eq!(String::from(&root), r#""#); + + let child = Element::builder("child", "ns2") + .prefix(None, "ns3") + .build(); + let root = Element::builder("root", "ns1") + .append(child) + .build(); + // TODO: Same remark as `writer_no_prefix_namespace`. + assert_eq!(String::from(&root), r#""#); +} + +#[test] +fn writer_prefix_namespace_child() { + let child = Element::builder("child", "ns1").build(); + let root = Element::builder("root", "ns1") + .prefix(Some(String::from("p1")), "ns1") + .append(child) + .build(); + assert_eq!(String::from(&root), r#""#); +} + +#[test] +fn writer_with_prefix_deduplicate() { + let child = Element::builder("child", "ns1") + // .prefix(Some(String::from("p1")), "ns1") + .build(); + let root = Element::builder("root", "ns1") + .prefix(Some(String::from("p1")), "ns1") + .prefix(None, "ns2") + .append(child) + .build(); + assert_eq!(String::from(&root), + r#""#, + ); + + // Ensure descendants don't just reuse ancestors' prefixes that have been shadowed in between + let grandchild = Element::builder("grandchild", "ns1") + .build(); + let child = Element::builder("child", "ns2") + .append(grandchild) + .build(); + let root = Element::builder("root", "ns1") + .append(child) + .build(); + assert_eq!(String::from(&root), + r#""#, + ); +} + #[test] fn writer_escapes_attributes() { - let root = Element::builder("root").attr("a", "\"Air\" quotes").build(); + let root = Element::builder("root", "ns1").attr("a", "\"Air\" quotes").build(); let mut writer = Vec::new(); { root.write_to(&mut writer).unwrap(); } assert_eq!( String::from_utf8(writer).unwrap(), - r#""# + r#""# ); } #[test] fn writer_escapes_text() { - let root = Element::builder("root").append("<3").build(); + let root = Element::builder("root", "ns1").append("<3").build(); let mut writer = Vec::new(); { root.write_to(&mut writer).unwrap(); } - assert_eq!(String::from_utf8(writer).unwrap(), r#"<3"#); + assert_eq!(String::from_utf8(writer).unwrap(), r#"<3"#); } #[test] fn builder_works() { - let elem = Element::builder("a") - .ns("b") + let elem = Element::builder("a", "b") .attr("c", "d") - .append(Element::builder("child")) + .append(Element::builder("child", "b")) .append("e") .build(); assert_eq!(elem.name(), "a"); - assert_eq!(elem.ns(), Some("b".to_owned())); + assert_eq!(elem.ns(), "b".to_owned()); assert_eq!(elem.attr("c"), Some("d")); assert_eq!(elem.attr("x"), None); assert_eq!(elem.text(), "e"); @@ -140,9 +288,9 @@ fn get_child_works() { #[test] fn namespace_propagation_works() { - let mut root = Element::builder("root").ns("root_ns").build(); - let mut child = Element::bare("child"); - let grandchild = Element::bare("grandchild"); + let mut root = Element::builder("root", "root_ns").build(); + let mut child = Element::bare("child", "root_ns"); + let grandchild = Element::bare("grandchild", "root_ns"); child.append_child(grandchild); root.append_child(child); @@ -159,12 +307,12 @@ fn namespace_propagation_works() { #[test] fn two_elements_with_same_arguments_different_order_are_equal() { - let elem1: Element = "".parse().unwrap(); - let elem2: Element = "".parse().unwrap(); + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); assert_eq!(elem1, elem2); - let elem1: Element = "".parse().unwrap(); - let elem2: Element = "".parse().unwrap(); + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); assert_ne!(elem1, elem2); } @@ -184,11 +332,11 @@ fn namespace_attributes_works() { #[test] fn wrongly_closed_elements_error() { - let elem1 = "".parse::(); + let elem1 = "".parse::(); assert!(elem1.is_err()); - let elem1 = "".parse::(); + let elem1 = "".parse::(); assert!(elem1.is_err()); - let elem1 = "".parse::(); + let elem1 = "".parse::(); assert!(elem1.is_ok()); } @@ -196,7 +344,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".to_owned())); + assert_eq!(elem.ns(), "jabber:client".to_owned()); } #[test] @@ -207,53 +355,53 @@ fn namespace_prefixed() { assert_eq!(elem.name(), "features"); assert_eq!( elem.ns(), - Some("http://etherx.jabber.org/streams".to_owned()) + "http://etherx.jabber.org/streams".to_owned(), ); } #[test] fn namespace_inherited_simple() { - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); assert_eq!(elem.name(), "stream"); - assert_eq!(elem.ns(), Some("jabber:client".to_owned())); + assert_eq!(elem.ns(), "jabber:client".to_owned()); let child = elem.children().next().unwrap(); assert_eq!(child.name(), "message"); - assert_eq!(child.ns(), Some("jabber:client".to_owned())); + assert_eq!(child.ns(), "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".to_owned()) + "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".to_owned())); + assert_eq!(child.ns(), "jabber:client".to_owned()); } #[test] fn namespace_inherited_prefixed2() { - let elem: Element = "" + let elem: Element = "" .parse().unwrap(); assert_eq!(elem.name(), "stream"); assert_eq!( elem.ns(), - Some("http://etherx.jabber.org/streams".to_owned()) + "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".to_owned())); + assert_eq!(child.ns(), "jabber:client".to_owned()); } #[test] fn fail_comments() { - let elem: Result = "".parse(); + let elem: Result = "".parse(); match elem { Err(Error::NoComments) => (), _ => panic!(), @@ -262,12 +410,12 @@ fn fail_comments() { #[test] fn xml_error() { - match "".parse::() { + match "".parse::() { Err(crate::error::Error::XmlError(_)) => (), err => panic!("No or wrong error: {:?}", err), } - match "() { + match "() { Err(crate::error::Error::XmlError(_)) => (), err => panic!("No or wrong error: {:?}", err), } @@ -280,3 +428,11 @@ fn invalid_element_error() { err => panic!("No or wrong error: {:?}", err), } } + +#[test] +fn missing_namespace_error() { + match "".parse::() { + Err(crate::error::Error::MissingNamespace) => (), + err => panic!("No or wrong error: {:?}", err), + } +}