Detailed changes
@@ -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<String>,
name: String,
- namespaces: Rc<NamespaceSet>,
+ namespace: String,
+ prefixes: Rc<Prefixes>,
attributes: BTreeMap<String, String>,
children: Vec<Node>,
}
@@ -121,17 +122,18 @@ impl PartialEq for Element {
}
impl Element {
- fn new<NS: Into<NamespaceSet>>(
+ fn new<P: Into<Prefixes>>(
name: String,
- prefix: Option<String>,
- namespaces: NS,
+ namespace: String,
+ prefixes: P,
attributes: BTreeMap<String, String>,
children: Vec<Node>,
) -> 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<S: AsRef<str>>(name: S) -> ElementBuilder {
+ pub fn builder<S: AsRef<str>, NS: Into<String>>(name: S, namespace: NS) -> ElementBuilder {
let (prefix, name) = split_element_name(name).unwrap();
+ let namespace: String = namespace.into();
+ let prefixes: BTreeMap<String, Option<String>> = match prefix {
+ None => Default::default(),
+ Some(_) => {
+ let mut prefixes: BTreeMap<String, Option<String>> = 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<S: Into<String>>(name: S) -> Element {
+ pub fn bare<S: Into<String>, NS: Into<String>>(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<String> {
- 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 = "<elem a=\"b\" />".parse().unwrap();
+ /// let elm: Element = "<elem xmlns=\"ns1\" a=\"b\" />".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<str>, NS: Into<NSChoice<'a>>>(&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<NSChoice<'a>>>(&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<R: BufRead>(reader: &mut EventReader<R>) -> Result<Element> {
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<W: Write>(&self, writer: &mut EventWriter<W>) -> 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<W: Write>(&self, writer: &mut EventWriter<W>) -> 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 `<?xml?>` prelude
- pub fn write_to_inner<W: Write>(&self, writer: &mut EventWriter<W>) -> 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<W: Write>(&self, writer: &mut EventWriter<W>, all_prefixes: &mut BTreeMap<Option<String>, 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<Option<String>> = 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<String>, 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 = "<root>a<c1 />b<c2 />c</root>".parse().unwrap();
+ /// let elem: Element = "<root xmlns=\"ns1\">a<c1 />b<c2 />c</root>".parse().unwrap();
///
/// let mut iter = elem.nodes();
///
@@ -511,7 +567,7 @@ impl Element {
/// ```rust
/// use minidom::Element;
///
- /// let elem: Element = "<root>hello<child1 />this<child2 />is<child3 />ignored</root>".parse().unwrap();
+ /// let elem: Element = "<root xmlns=\"ns1\">hello<child1 xmlns=\"ns1\"/>this<child2 xmlns=\"ns1\"/>is<child3 xmlns=\"ns1\"/>ignored</root>".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 = "<root>hello<c /> world!</root>".parse().unwrap();
+ /// let elem: Element = "<root xmlns=\"ns1\">hello<c /> world!</root>".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 = "<node>hello,<split /> world!</node>".parse().unwrap();
+ /// let elem: Element = "<node xmlns=\"ns1\">hello,<split /> world!</node>".parse().unwrap();
///
/// assert_eq!(elem.text(), "hello, world!");
/// ```
@@ -656,7 +710,7 @@ impl Element {
/// ```rust
/// use minidom::{Element, NSChoice};
///
- /// let elem: Element = r#"<node xmlns="ns"><a /><a xmlns="other_ns" /><b /></node>"#.parse().unwrap();
+ /// let elem: Element = r#"<node xmlns="ns"><a/><a xmlns="other_ns" /><b/></node>"#.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: AsRef<str>>(s: S) -> Result<(Option<String>, String)> {
}
}
-fn build_element<R: BufRead>(reader: &EventReader<R>, event: &BytesStart) -> Result<Element> {
- let mut namespaces = BTreeMap::new();
+fn build_element<R: BufRead>(reader: &EventReader<R>, event: &BytesStart, prefixes: &mut BTreeMap<String, Option<String>>) -> Result<Element> {
+ 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<R: BufRead>(reader: &EventReader<R>, 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::<Result<BTreeMap<String, String>>>()?;
- 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<String> = 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<Option<String>, String>,
+ prefixes: BTreeMap<String, Option<String>>,
}
impl ElementBuilder {
- /// Sets the namespace.
- pub fn ns<S: Into<String>>(mut self, namespace: S) -> ElementBuilder {
- self.namespaces
- .insert(self.root.prefix.clone(), namespace.into());
+ /// Sets a custom prefix.
+ pub fn prefix<S: Into<String>>(mut self, prefix: Option<String>, 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 = "<foo></foo>";
+ let xml = "<foo xmlns='ns1'></foo>";
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 = "<foo><bar baz='qxx' /></foo>";
+ let xml = "<foo xmlns='ns1'><bar xmlns='ns1' baz='qxx' /></foo>";
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 = "<foo><prefix:bar baz='qxx' /></foo>";
+ let xml = "<foo xmlns='ns1'><prefix:bar xmlns:prefix='ns1' baz='qxx' /></foo>";
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 = "<test><![CDATA['>blah<blah>]]></test>";
+ let xml = "<test xmlns='test'><![CDATA['>blah<blah>]]></test>";
let mut reader = EventReader::from_str(xml);
let elem = Element::from_reader(&mut reader).unwrap();
assert_eq!(elem.text(), "'>blah<blah>");
@@ -1030,7 +1093,7 @@ mod tests {
#[test]
fn test_compare_all_ns() {
- let xml = "<foo xmlns='foo' xmlns:bar='baz'><bar:meh/></foo>";
+ let xml = "<foo xmlns='foo' xmlns:bar='baz'><bar:meh xmlns:bar='baz' /></foo>";
let mut reader = EventReader::from_str(xml);
let elem = Element::from_reader(&mut reader).unwrap();
@@ -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"
@@ -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;
@@ -1,217 +0,0 @@
-// Copyright (c) 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
-// Copyright (c) 2020 Astro <astro@spaceboyz.net>
-// Copyright (c) 2020 Maxime “pep” Buquet <pep@bouah.net>
-// Copyright (c) 2020 Xidorn Quan <me@upsuper.org>
-//
-// 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<Option<Rc<NamespaceSet>>>,
- namespaces: BTreeMap<Option<String>, 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<Option<String>, String> {
- &self.namespaces
- }
-
- pub fn get(&self, prefix: &Option<String>) -> Option<String> {
- 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<NSChoice<'a>>>(&self, prefix: &Option<String>, 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<NamespaceSet>) {
- let mut parent_ns = self.parent.borrow_mut();
- let new_set = parent;
- *parent_ns = Some(new_set);
- }
-}
-
-impl From<BTreeMap<Option<String>, String>> for NamespaceSet {
- fn from(namespaces: BTreeMap<Option<String>, String>) -> Self {
- NamespaceSet {
- parent: RefCell::new(None),
- namespaces,
- }
- }
-}
-
-impl From<Option<String>> for NamespaceSet {
- fn from(namespace: Option<String>) -> Self {
- match namespace {
- None => Self::default(),
- Some(namespace) => Self::from(namespace),
- }
- }
-}
-
-impl From<String> 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>, String)> for NamespaceSet {
- fn from(prefix_namespace: (Option<String>, 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)))");
- }
-}
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+// Copyright (c) 2020 Astro <astro@spaceboyz.net>
+// Copyright (c) 2020 Maxime “pep” Buquet <pep@bouah.net>
+// Copyright (c) 2020 Xidorn Quan <me@upsuper.org>
+//
+// 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),
+ }
+ }
+}
@@ -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("<meow />".parse().unwrap());
+ /// let elm = Node::Element("<meow xmlns=\"ns1\"/>".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("<meow />".parse().unwrap());
+ /// let mut elm = Node::Element("<meow xmlns=\"ns1\"/>".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("<meow />".parse().unwrap());
+ /// let elm = Node::Element("<meow xmlns=\"ns1\"/>".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("<meow />".parse().unwrap());
+ /// let elm = Node::Element("<meow xmlns=\"ns1\"/>".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("<meow />".parse().unwrap());
+ /// let mut elm = Node::Element("<meow xmlns=\"ns1\"/>".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("<meow />".parse().unwrap());
+ /// let elm = Node::Element("<meow xmlns=\"ns1\"/>".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<W: Write>(&self, writer: &mut EventWriter<W>) -> Result<()> {
+ pub(crate) fn write_to_inner<W: Write>(&self, writer: &mut EventWriter<W>, prefixes: &mut BTreeMap<Option<String>, 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)))?;
}
@@ -0,0 +1,184 @@
+// Copyright (c) 2020 Maxime “pep” Buquet <pep@bouah.net>
+// Copyright (c) 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+//
+// 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<BytesMut>,
+ 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<Element>,
+
+ /// 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 <stream:stream> 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("<?xml") {
+ let mut stuff = string.splitn(2, '>');
+ 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<Option<ParserResult>> {
+ 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"<?xml version='1.0'?>"[..]);
+ buf.put(&b"<stream:stream xmlns='jabber:client' xml:lang='en' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='foo.bar'>"[..]);
+ 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!(),
+ }
+ }
+}
@@ -0,0 +1,96 @@
+// Copyright (c) 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+// Copyright (c) 2020 Astro <astro@spaceboyz.net>
+// Copyright (c) 2020 Maxime “pep” Buquet <pep@bouah.net>
+// Copyright (c) 2020 Xidorn Quan <me@upsuper.org>
+//
+// 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<String, Option<String>>,
+}
+
+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<String, Option<String>> {
+ &self.prefixes
+ }
+
+ pub fn get(&self, namespace: &String) -> Option<Option<String>> {
+ match self.prefixes.get(namespace) {
+ Some(ns) => Some(ns.clone()),
+ None => None,
+ }
+ }
+}
+
+impl From<BTreeMap<String, Option<String>>> for Prefixes {
+ fn from(prefixes: BTreeMap<String, Option<String>>) -> Self {
+ Prefixes { prefixes }
+ }
+}
+
+impl From<Option<String>> for Prefixes {
+ fn from(namespace: Option<String>) -> Self {
+ match namespace {
+ None => Self::default(),
+ Some(namespace) => Self::from(namespace),
+ }
+ }
+}
+
+impl From<String> for Prefixes {
+ fn from(namespace: String) -> Self {
+ let mut prefixes = BTreeMap::new();
+ prefixes.insert(namespace, None);
+
+ Prefixes { prefixes }
+ }
+}
+
+impl From<(Option<String>, String)> for Prefixes {
+ fn from(prefix_namespace: (Option<String>, 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))
+ }
+}
@@ -18,16 +18,14 @@ use quick_xml::Reader;
const TEST_STRING: &'static str = r#"<root xmlns="root_ns" a="b" xml:lang="en">meow<child c="d"/><child xmlns="child_ns" d="e" xml:lang="fr"/>nya</root>"#;
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#"<root xmlns="ns1"><child/></root>"#.parse().unwrap();
+ let _: Element = r#"<p1:root xmlns:p1="ns1"><p1:child/></p1:root>"#.parse().unwrap();
+ let _: Element = r#"<root xmlns="ns1"><child xmlns:p1="ns2"><p1:grandchild/></child></root>"#.parse().unwrap();
+
+ match r#"<p1:root xmlns:p1="ns1"><child/></p1:root>"#.parse::<Element>() {
+ 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#"<root xmlns="ns1"><p1:child1 xmlns:p1="ns2"/><p1:child2/></root>"#.parse::<Element>() {
+ 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#"<p1:root xmlns:p1="ns1" xmlns="ns2"/>"#,
+ );
+}
+
+#[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#"<root xmlns="ns1"/>"#);
+}
+
+#[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#"<root xmlns="ns1"><child/></root>"#);
+
+ 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#"<root xmlns="ns1"><ns0:child xmlns:ns0="ns2" xmlns="ns3"/></root>"#);
+}
+
+#[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#"<p1:root xmlns:p1="ns1"><p1:child/></p1:root>"#);
+}
+
+#[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#"<p1:root xmlns:p1="ns1" xmlns="ns2"><p1:child/></p1:root>"#,
+ );
+
+ // 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#"<root xmlns="ns1"><child xmlns="ns2"><grandchild xmlns="ns1"/></child></root>"#,
+ );
+}
+
#[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#"<root a=""Air" quotes"/>"#
+ r#"<root xmlns="ns1" a=""Air" quotes"/>"#
);
}
#[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#"<root><3</root>"#);
+ assert_eq!(String::from_utf8(writer).unwrap(), r#"<root xmlns="ns1"><3</root>"#);
}
#[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 = "<a b='a' c=''/>".parse().unwrap();
- let elem2: Element = "<a c='' b='a'/>".parse().unwrap();
+ let elem1: Element = "<a b='a' c='' xmlns='ns1'/>".parse().unwrap();
+ let elem2: Element = "<a c='' b='a' xmlns='ns1'/>".parse().unwrap();
assert_eq!(elem1, elem2);
- let elem1: Element = "<a b='a' c=''/>".parse().unwrap();
- let elem2: Element = "<a c='d' b='a'/>".parse().unwrap();
+ let elem1: Element = "<a b='a' c='' xmlns='ns1'/>".parse().unwrap();
+ let elem2: Element = "<a c='d' b='a' xmlns='ns1'/>".parse().unwrap();
assert_ne!(elem1, elem2);
}
@@ -184,11 +332,11 @@ fn namespace_attributes_works() {
#[test]
fn wrongly_closed_elements_error() {
- let elem1 = "<a></b>".parse::<Element>();
+ let elem1 = "<a xmlns='ns1'></b>".parse::<Element>();
assert!(elem1.is_err());
- let elem1 = "<a></c></a>".parse::<Element>();
+ let elem1 = "<a xmlns='ns1'></c></a>".parse::<Element>();
assert!(elem1.is_err());
- let elem1 = "<a><c><d/></c></a>".parse::<Element>();
+ let elem1 = "<a xmlns='ns1'><c xmlns='ns1'><d xmlns='ns1'/></c></a>".parse::<Element>();
assert!(elem1.is_ok());
}
@@ -196,7 +344,7 @@ fn wrongly_closed_elements_error() {
fn namespace_simple() {
let elem: Element = "<message xmlns='jabber:client'/>".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 = "<stream xmlns='jabber:client'><message/></stream>"
+ let elem: Element = "<stream xmlns='jabber:client'><message xmlns='jabber:client' /></stream>"
.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 = "<stream:features xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client'><message/></stream:features>"
+ let elem: Element = "<stream:features xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client'><message xmlns='jabber:client' /></stream:features>"
.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 = "<stream xmlns='http://etherx.jabber.org/streams' xmlns:jabber='jabber:client'><jabber:message/></stream>"
+ let elem: Element = "<stream xmlns='http://etherx.jabber.org/streams' xmlns:jabber='jabber:client'><jabber:message xmlns:jabber='jabber:client' /></stream>"
.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<Element, Error> = "<foo><!-- bar --></foo>".parse();
+ let elem: Result<Element, Error> = "<foo xmlns='ns1'><!-- bar --></foo>".parse();
match elem {
Err(Error::NoComments) => (),
_ => panic!(),
@@ -262,12 +410,12 @@ fn fail_comments() {
#[test]
fn xml_error() {
- match "<a></b>".parse::<Element>() {
+ match "<a xmlns='ns1'></b>".parse::<Element>() {
Err(crate::error::Error::XmlError(_)) => (),
err => panic!("No or wrong error: {:?}", err),
}
- match "<a></".parse::<Element>() {
+ match "<a xmlns='ns1'></".parse::<Element>() {
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 "<a/>".parse::<Element>() {
+ Err(crate::error::Error::MissingNamespace) => (),
+ err => panic!("No or wrong error: {:?}", err),
+ }
+}