minidom, tokio-xmpp: switch xml parsing to rxml

Astro created

Change summary

minidom/Cargo.toml                    |   1 
minidom/src/element.rs                | 263 ++++-----------------------
minidom/src/error.rs                  |  45 ---
minidom/src/lib.rs                    |   1 
minidom/src/parser.rs                 |  15 
minidom/src/tests.rs                  |  35 +--
minidom/src/tree_builder.rs           | 154 ++++++++++++++++
parsers/src/caps.rs                   |   6 
parsers/src/cert_management.rs        |   3 
parsers/src/ecaps2.rs                 |   6 
parsers/src/ibr.rs                    |   3 
parsers/src/jingle_ft.rs              |  12 
parsers/src/jingle_grouping.rs        |   3 
parsers/src/jingle_ice_udp.rs         |   6 
parsers/src/jingle_raw_udp.rs         |   3 
parsers/src/jingle_rtp.rs             |   3 
parsers/src/jingle_rtp_hdrext.rs      |   3 
parsers/src/jingle_ssma.rs            |   6 
parsers/src/legacy_omemo.rs           |   9 
parsers/src/mam.rs                    |  15 -
parsers/src/mam_prefs.rs              |   6 
parsers/src/media_element.rs          |   6 
parsers/src/muc/muc.rs                |   6 
parsers/src/muc/user.rs               | 259 ++++++++++----------------
parsers/src/roster.rs                 |  23 -
parsers/src/sm.rs                     |   2 
parsers/src/time.rs                   |   2 
tokio-xmpp/Cargo.toml                 |   3 
tokio-xmpp/src/client/async_client.rs |  44 ++--
tokio-xmpp/src/error.rs               |  51 ----
tokio-xmpp/src/lib.rs                 |   2 
tokio-xmpp/src/xmpp_codec.rs          | 276 +++++-----------------------
tokio-xmpp/src/xmpp_stream.rs         |   4 
33 files changed, 465 insertions(+), 811 deletions(-)

Detailed changes

minidom/Cargo.toml πŸ”—

@@ -22,3 +22,4 @@ gitlab = { repository = "xmpp-rs/xmpp-rs" }
 
 [dependencies]
 quick-xml = "0.22.0"
+rxml = "^0.7.1"

minidom/src/element.rs πŸ”—

@@ -17,18 +17,17 @@ use crate::error::{Error, Result};
 use crate::namespaces::NSChoice;
 use crate::node::Node;
 use crate::prefixes::{Namespace, Prefix, Prefixes};
+use crate::tree_builder::TreeBuilder;
 
 use std::collections::{btree_map, BTreeMap};
-use std::io::Write;
+use std::io::{BufRead, Write};
 
 use std::borrow::Cow;
 use std::str;
 
 use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, Event};
-use quick_xml::Reader as EventReader;
 use quick_xml::Writer as EventWriter;
-
-use std::io::BufRead;
+use rxml::{EventRead, Lexer, PullDriver, RawParser};
 
 use std::str::FromStr;
 
@@ -84,8 +83,9 @@ pub struct Element {
     namespace: String,
     /// This is only used when deserializing. If you have to use a custom prefix use
     /// `ElementBuilder::prefix`.
-    prefix: Option<Prefix>,
-    prefixes: Prefixes,
+    pub(crate) prefix: Option<Prefix>,
+    /// Namespace declarations
+    pub prefixes: Prefixes,
     attributes: BTreeMap<String, String>,
     children: Vec<Node>,
 }
@@ -102,8 +102,7 @@ impl FromStr for Element {
     type Err = Error;
 
     fn from_str(s: &str) -> Result<Element> {
-        let mut reader = EventReader::from_str(s);
-        Element::from_reader(&mut reader)
+        Element::from_reader(s.as_bytes())
     }
 }
 
@@ -120,15 +119,8 @@ impl PartialEq for Element {
     }
 }
 
-fn ensure_no_prefix<S: AsRef<str>>(s: &S) -> Result<()> {
-    match s.as_ref().split(':').count() {
-        1 => Ok(()),
-        _ => Err(Error::InvalidElement),
-    }
-}
-
 impl Element {
-    fn new<P: Into<Prefixes>>(
+    pub(crate) fn new<P: Into<Prefixes>>(
         name: String,
         namespace: String,
         prefix: Option<Prefix>,
@@ -136,8 +128,6 @@ impl Element {
         attributes: BTreeMap<String, String>,
         children: Vec<Node>,
     ) -> Element {
-        ensure_no_prefix(&name).unwrap();
-        // TODO: Return Result<Element> instead.
         Element {
             name,
             namespace,
@@ -310,123 +300,18 @@ impl Element {
         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, &mut prefixes)?;
-                }
-                Event::Eof => {
-                    return Err(Error::EndOfDocument);
-                }
-                Event::Comment { .. } => {
-                    return Err(Error::NoComments);
-                }
-                Event::Text { .. }
-                | Event::End { .. }
-                | Event::CData { .. }
-                | Event::Decl { .. }
-                | Event::PI { .. }
-                | Event::DocType { .. } => (), // TODO: may need more errors
-            }
-        };
-
-        let mut stack = vec![root];
-        let mut prefix_stack = vec![prefixes];
+    /// Parse a document from a `BufRead`.
+    pub fn from_reader<R: BufRead>(reader: R) -> Result<Element> {
+        let mut tree_builder = TreeBuilder::new();
+        let mut driver = PullDriver::wrap(reader, Lexer::new(), RawParser::new());
+        while let Some(event) = driver.read()? {
+            tree_builder.process_event(event)?;
 
-        loop {
-            match reader.read_event(&mut buf)? {
-                Event::Empty(ref 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 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;
-                    }
-                    let prefixes = 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
-                        let elem_name = e.name();
-                        let mut split_iter = elem_name.splitn(2, |u| *u == 0x3A);
-                        let possible_prefix = split_iter.next().unwrap(); // Can't be empty.
-                        let opening_prefix = {
-                            let mut tmp: Option<Option<String>> = None;
-                            for (prefix, ns) in prefixes {
-                                if ns == elem.namespace {
-                                    tmp = Some(prefix.clone());
-                                    break;
-                                }
-                            }
-                            match tmp {
-                                Some(prefix) => prefix,
-                                None => return Err(Error::InvalidPrefix),
-                            }
-                        };
-                        match split_iter.next() {
-                            // There is a prefix on the closing tag
-                            Some(name) => {
-                                // Does the closing prefix match the opening prefix?
-                                match opening_prefix {
-                                    Some(prefix) if possible_prefix == prefix.as_bytes() => (),
-                                    _ => return Err(Error::InvalidElementClosed),
-                                }
-                                // Does the closing tag name match the opening tag name?
-                                if name != elem.name().as_bytes() {
-                                    return Err(Error::InvalidElementClosed);
-                                }
-                            }
-                            // There was no prefix on the closing tag
-                            None => {
-                                // Is there a prefix on the opening tag?
-                                if opening_prefix.is_some() {
-                                    return Err(Error::InvalidElementClosed);
-                                }
-                                // Does the opening tag name match the closing one?
-                                if possible_prefix != elem.name().as_bytes() {
-                                    return Err(Error::InvalidElementClosed);
-                                }
-                            }
-                        }
-                        to.append_child(elem);
-                    }
-                }
-                Event::Text(s) => {
-                    let text = s.unescape_and_decode(reader)?;
-                    if !text.is_empty() {
-                        let current_elem = stack.last_mut().unwrap();
-                        current_elem.append_text_node(text);
-                    }
-                }
-                Event::CData(s) => {
-                    let text = s.unescape_and_decode(&reader)?;
-                    if !text.is_empty() {
-                        let current_elem = stack.last_mut().unwrap();
-                        current_elem.append_text_node(text);
-                    }
-                }
-                Event::Eof => {
-                    break;
-                }
-                Event::Comment(_) => return Err(Error::NoComments),
-                Event::Decl { .. } | Event::PI { .. } | Event::DocType { .. } => (),
+            if let Some(root) = tree_builder.root.take() {
+                return Ok(root);
             }
         }
-        Ok(stack.pop().unwrap())
+        Err(Error::EndOfDocument)
     }
 
     /// Output a document to a `Writer`.
@@ -822,68 +707,18 @@ impl Element {
         })?;
         self.children.remove(idx).into_element()
     }
-}
-
-fn split_element_name<S: AsRef<str>>(s: S) -> Result<(Option<String>, String)> {
-    let name_parts = s.as_ref().split(':').collect::<Vec<&str>>();
-    match name_parts.len() {
-        2 => Ok((Some(name_parts[0].to_owned()), name_parts[1].to_owned())),
-        1 => Ok((None, name_parts[0].to_owned())),
-        _ => Err(Error::InvalidElement),
-    }
-}
 
-fn build_element<R: BufRead>(
-    reader: &EventReader<R>,
-    event: &BytesStart,
-    prefixes: &mut BTreeMap<Prefix, Namespace>,
-) -> 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| {
-            let o = o?;
-            let key = str::from_utf8(o.key)?.to_owned();
-            let value = o.unescape_and_decode_value(reader)?;
-            Ok((key, value))
-        })
-        .filter(|o| match *o {
-            Ok((ref key, ref value)) if key == "xmlns" => {
-                local_prefixes.insert(None, value.clone());
-                prefixes.insert(None, value.clone());
-                false
-            }
-            Ok((ref key, ref value)) if key.starts_with("xmlns:") => {
-                local_prefixes.insert(Some(key[6..].to_owned()), value.to_owned());
-                prefixes.insert(Some(key[6..].to_owned()), value.to_owned());
-                false
+    /// Remove the leading nodes up to the first child element and
+    /// return it
+    pub fn unshift_child(&mut self) -> Option<Element> {
+        while self.children.len() > 0 {
+            if let Some(el) = self.children.remove(0).into_element() {
+                return Some(el);
             }
-            _ => true,
-        })
-        .collect::<Result<BTreeMap<String, String>>>()?;
-
-    let namespace: &String = {
-        if let Some(namespace) = local_prefixes.get(&prefix) {
-            namespace
-        } else if let Some(namespace) = prefixes.get(&prefix) {
-            namespace
-        } else {
-            return Err(Error::MissingNamespace);
         }
-    };
-
-    Ok(Element::new(
-        name,
-        namespace.clone(),
-        // Note that this will always be Some(_) as we can't distinguish between the None case and
-        // Some(None). At least we make sure the prefix has a namespace associated.
-        Some(prefix),
-        local_prefixes,
-        attributes,
-        Vec::new(),
-    ))
+
+        None
+    }
 }
 
 /// An iterator over references to child elements of an `Element`.
@@ -1067,9 +902,8 @@ mod tests {
 
     #[test]
     fn test_from_reader_simple() {
-        let xml = "<foo xmlns='ns1'></foo>";
-        let mut reader = EventReader::from_str(xml);
-        let elem = Element::from_reader(&mut reader);
+        let xml = b"<foo xmlns='ns1'></foo>";
+        let elem = Element::from_reader(&xml[..]);
 
         let elem2 = Element::builder("foo", "ns1").build();
 
@@ -1078,9 +912,8 @@ mod tests {
 
     #[test]
     fn test_from_reader_nested() {
-        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 xml = b"<foo xmlns='ns1'><bar xmlns='ns1' baz='qxx' /></foo>";
+        let elem = Element::from_reader(&xml[..]);
 
         let nested = Element::builder("bar", "ns1").attr("baz", "qxx").build();
         let elem2 = Element::builder("foo", "ns1").append(nested).build();
@@ -1090,9 +923,8 @@ mod tests {
 
     #[test]
     fn test_from_reader_with_prefix() {
-        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 xml = b"<foo xmlns='ns1'><prefix:bar xmlns:prefix='ns1' baz='qxx' /></foo>";
+        let elem = Element::from_reader(&xml[..]);
 
         let nested = Element::builder("bar", "ns1").attr("baz", "qxx").build();
         let elem2 = Element::builder("foo", "ns1").append(nested).build();
@@ -1102,9 +934,8 @@ mod tests {
 
     #[test]
     fn test_from_reader_split_prefix() {
-        let xml = "<foo:bar xmlns:foo='ns1'/>";
-        let mut reader = EventReader::from_str(xml);
-        let elem = Element::from_reader(&mut reader).unwrap();
+        let xml = b"<foo:bar xmlns:foo='ns1'/>";
+        let elem = Element::from_reader(&xml[..]).unwrap();
 
         assert_eq!(elem.name(), String::from("bar"));
         assert_eq!(elem.ns(), String::from("ns1"));
@@ -1118,38 +949,32 @@ mod tests {
     #[test]
     fn parses_spectest_xml() {
         // From: https://gitlab.com/lumi/minidom-rs/issues/8
-        let xml = r#"
-            <rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
+        let xml = br#"<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
                 <rng:name xmlns:rng="http://relaxng.org/ns/structure/1.0"></rng:name>
             </rng:grammar>
         "#;
-        let mut reader = EventReader::from_str(xml);
-        let _ = Element::from_reader(&mut reader).unwrap();
+        let _ = Element::from_reader(&xml[..]).unwrap();
     }
 
     #[test]
     fn does_not_unescape_cdata() {
-        let xml = "<test xmlns='test'><![CDATA[&apos;&gt;blah<blah>]]></test>";
-        let mut reader = EventReader::from_str(xml);
-        let elem = Element::from_reader(&mut reader).unwrap();
+        let xml = b"<test xmlns='test'><![CDATA[&apos;&gt;blah<blah>]]></test>";
+        let elem = Element::from_reader(&xml[..]).unwrap();
         assert_eq!(elem.text(), "&apos;&gt;blah<blah>");
     }
 
     #[test]
     fn test_compare_all_ns() {
-        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();
+        let xml = b"<foo xmlns='foo' xmlns:bar='baz'><bar:meh xmlns:bar='baz' /></foo>";
+        let elem = Element::from_reader(&xml[..]).unwrap();
 
         let elem2 = elem.clone();
 
-        let xml3 = "<foo xmlns='foo'><bar:meh xmlns:bar='baz'/></foo>";
-        let mut reader3 = EventReader::from_str(xml3);
-        let elem3 = Element::from_reader(&mut reader3).unwrap();
+        let xml3 = b"<foo xmlns='foo'><bar:meh xmlns:bar='baz'/></foo>";
+        let elem3 = Element::from_reader(&xml3[..]).unwrap();
 
-        let xml4 = "<prefix:foo xmlns:prefix='foo'><bar:meh xmlns:bar='baz'/></prefix:foo>";
-        let mut reader4 = EventReader::from_str(xml4);
-        let elem4 = Element::from_reader(&mut reader4).unwrap();
+        let xml4 = b"<prefix:foo xmlns:prefix='foo'><bar:meh xmlns:bar='baz'/></prefix:foo>";
+        let elem4 = Element::from_reader(&xml4[..]).unwrap();
 
         assert_eq!(elem, elem2);
         assert_eq!(elem, elem3);

minidom/src/error.rs πŸ”—

@@ -20,21 +20,12 @@ pub enum Error {
     /// An error from quick_xml.
     XmlError(::quick_xml::Error),
 
-    /// An UTF-8 conversion error.
-    Utf8Error(::std::str::Utf8Error),
-
-    /// An I/O error, from std::io.
-    IoError(::std::io::Error),
+    /// Error from rxml parsing
+    ParserError(rxml::Error),
 
     /// An error which is returned when the end of the document was reached prematurely.
     EndOfDocument,
 
-    /// An error which is returned when an element is closed when it shouldn't be
-    InvalidElementClosed,
-
-    /// An error which is returned when an elemet's name contains more colons than permitted
-    InvalidElement,
-
     /// An error which is returned when an element being serialized doesn't contain a prefix
     /// (be it None or Some(_)).
     InvalidPrefix,
@@ -42,9 +33,6 @@ pub enum Error {
     /// 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,
-
     /// An error which is returned when a prefixed is defined twice
     DuplicatePrefix,
 }
@@ -53,14 +41,10 @@ impl StdError for Error {
     fn cause(&self) -> Option<&dyn StdError> {
         match self {
             Error::XmlError(e) => Some(e),
-            Error::Utf8Error(e) => Some(e),
-            Error::IoError(e) => Some(e),
+            Error::ParserError(e) => Some(e),
             Error::EndOfDocument => None,
-            Error::InvalidElementClosed => None,
-            Error::InvalidElement => None,
             Error::InvalidPrefix => None,
             Error::MissingNamespace => None,
-            Error::NoComments => None,
             Error::DuplicatePrefix => None,
         }
     }
@@ -70,21 +54,12 @@ impl std::fmt::Display for Error {
     fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
         match self {
             Error::XmlError(e) => write!(fmt, "XML error: {}", e),
-            Error::Utf8Error(e) => write!(fmt, "UTF-8 error: {}", e),
-            Error::IoError(e) => write!(fmt, "IO error: {}", e),
+            Error::ParserError(e) => write!(fmt, "XML parser error: {}", e),
             Error::EndOfDocument => {
                 write!(fmt, "the end of the document has been reached prematurely")
             }
-            Error::InvalidElementClosed => {
-                write!(fmt, "the XML is invalid, an element was wrongly closed")
-            }
-            Error::InvalidElement => write!(fmt, "the XML element is invalid"),
             Error::InvalidPrefix => write!(fmt, "the prefix 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"
-            ),
             Error::DuplicatePrefix => write!(fmt, "the prefix is already defined"),
         }
     }
@@ -96,15 +71,9 @@ impl From<::quick_xml::Error> for Error {
     }
 }
 
-impl From<::std::str::Utf8Error> for Error {
-    fn from(err: ::std::str::Utf8Error) -> Error {
-        Error::Utf8Error(err)
-    }
-}
-
-impl From<::std::io::Error> for Error {
-    fn from(err: ::std::io::Error) -> Error {
-        Error::IoError(err)
+impl From<rxml::Error> for Error {
+    fn from(err: rxml::Error) -> Error {
+        Error::ParserError(err)
     }
 }
 

minidom/src/lib.rs πŸ”—

@@ -83,6 +83,7 @@ pub mod error;
 mod namespaces;
 pub mod node;
 mod prefixes;
+pub mod tree_builder;
 
 #[cfg(test)]
 mod tests;

minidom/src/parser.rs πŸ”—

@@ -10,16 +10,16 @@
 
 use crate::element::Element;
 use crate::error::{Error, ParserError, Result};
+use crate::tree_builder::TreeBuilder;
 
-use bytes::BytesMut;
-use quick_xml::Reader as EventReader;
-use std::cell::RefCell;
+use rxml::{PushDriver, RawParser};
 use std::str;
 
 /// Parser
 #[derive(Debug)]
 pub struct Parser {
-    buffer: RefCell<BytesMut>,
+    driver: PushDriver<RawParser>,
+    tree_builder: TreeBuilder,
     state: ParserState,
 }
 
@@ -90,14 +90,17 @@ impl Parser {
     /// Creates a new Parser
     pub fn new() -> Parser {
         Parser {
-            buffer: RefCell::new(BytesMut::new()),
+            driver: PushDriver::default(),
+            tree_builder: TreeBuilder::new(),
             state: ParserState::Empty,
         }
     }
 
     /// Feed bytes to the parser.
     pub fn feed(&mut self, bytes: BytesMut) -> Result<()> {
-        self.buffer.borrow_mut().unsplit(bytes);
+        self.driver.feed(bytes);
+        bytes.clear();
+
         let state = match self.state {
             ParserState::Empty => {
                 // TODO: Try splitting xml prolog and stream header

minidom/src/tests.rs πŸ”—

@@ -13,9 +13,7 @@
 use crate::element::Element;
 use crate::error::Error;
 
-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>"#;
+const TEST_STRING: &'static [u8] = br#"<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", "root_ns")
@@ -36,9 +34,8 @@ fn build_test_tree() -> Element {
 
 #[test]
 fn reader_works() {
-    let mut reader = Reader::from_str(TEST_STRING);
     assert_eq!(
-        Element::from_reader(&mut reader).unwrap(),
+        Element::from_reader(TEST_STRING).unwrap(),
         build_test_tree()
     );
 }
@@ -143,7 +140,7 @@ fn writer_works() {
     {
         root.write_to(&mut writer).unwrap();
     }
-    assert_eq!(String::from_utf8(writer).unwrap(), TEST_STRING);
+    assert_eq!(writer, TEST_STRING);
 }
 
 #[test]
@@ -153,7 +150,10 @@ fn writer_with_decl_works() {
     {
         root.write_to_decl(&mut writer).unwrap();
     }
-    let result = format!(r#"<?xml version="1.0" encoding="utf-8"?>{}"#, TEST_STRING);
+    let result = format!(
+        r#"<?xml version="1.0" encoding="utf-8"?>{}"#,
+        String::from_utf8(TEST_STRING.to_owned()).unwrap()
+    );
     assert_eq!(String::from_utf8(writer).unwrap(), result);
 }
 
@@ -348,8 +348,7 @@ fn two_elements_with_same_arguments_different_order_are_equal() {
 
 #[test]
 fn namespace_attributes_works() {
-    let mut reader = Reader::from_str(TEST_STRING);
-    let root = Element::from_reader(&mut reader).unwrap();
+    let root = Element::from_reader(TEST_STRING).unwrap();
     assert_eq!("en", root.attr("xml:lang").unwrap());
     assert_eq!(
         "fr",
@@ -424,7 +423,7 @@ fn namespace_inherited_prefixed2() {
 fn fail_comments() {
     let elem: Result<Element, Error> = "<foo xmlns='ns1'><!-- bar --></foo>".parse();
     match elem {
-        Err(Error::NoComments) => (),
+        Err(_) => (),
         _ => panic!(),
     };
 }
@@ -432,20 +431,16 @@ fn fail_comments() {
 #[test]
 fn xml_error() {
     match "<a xmlns='ns1'></b>".parse::<Element>() {
-        Err(crate::error::Error::XmlError(_)) => (),
+        Err(crate::error::Error::ParserError(rxml::Error::NotWellFormed(
+            rxml::error::WFError::ElementMismatch,
+        ))) => (),
         err => panic!("No or wrong error: {:?}", err),
     }
 
     match "<a xmlns='ns1'></".parse::<Element>() {
-        Err(crate::error::Error::XmlError(_)) => (),
-        err => panic!("No or wrong error: {:?}", err),
-    }
-}
-
-#[test]
-fn invalid_element_error() {
-    match "<a:b:c>".parse::<Element>() {
-        Err(crate::error::Error::InvalidElement) => (),
+        Err(crate::error::Error::ParserError(rxml::Error::NotWellFormed(
+            rxml::error::WFError::InvalidEof(_),
+        ))) => (),
         err => panic!("No or wrong error: {:?}", err),
     }
 }

minidom/src/tree_builder.rs πŸ”—

@@ -0,0 +1,154 @@
+// Copyright (c) 2022 Astro <astro@spaceboyz.net>
+
+//! SAX events to DOM tree conversion
+
+use crate::prefixes::{Prefix, Prefixes};
+use crate::{Element, Error};
+use rxml::RawEvent;
+use std::collections::BTreeMap;
+
+/// Tree-building parser state
+pub struct TreeBuilder {
+    next_tag: Option<(Prefix, String, Prefixes, BTreeMap<String, String>)>,
+    /// Parsing stack
+    stack: Vec<Element>,
+    /// Namespace set stack by prefix
+    prefixes_stack: Vec<Prefixes>,
+    /// Document root element if finished
+    pub root: Option<Element>,
+}
+
+impl TreeBuilder {
+    /// Create a new one
+    pub fn new() -> Self {
+        TreeBuilder {
+            next_tag: None,
+            stack: vec![],
+            prefixes_stack: vec![],
+            root: None,
+        }
+    }
+
+    /// Stack depth
+    pub fn depth(&self) -> usize {
+        self.stack.len()
+    }
+
+    /// Get the top-most element from the stack but don't remove it
+    pub fn top(&mut self) -> Option<&Element> {
+        self.stack.last()
+    }
+
+    /// Pop the top-most element from the stack
+    fn pop(&mut self) -> Option<Element> {
+        self.prefixes_stack.pop();
+        self.stack.pop()
+    }
+
+    /// Unshift the first child of the top element
+    pub fn unshift_child(&mut self) -> Option<Element> {
+        let depth = self.stack.len();
+        if depth > 0 {
+            self.stack[depth - 1].unshift_child()
+        } else {
+            None
+        }
+    }
+
+    /// Lookup XML namespace declaration for given prefix (or no prefix)
+    fn lookup_prefix(&self, prefix: &Option<String>) -> Option<&str> {
+        for nss in self.prefixes_stack.iter().rev() {
+            if let Some(ns) = nss.get(prefix) {
+                return Some(ns);
+            }
+        }
+
+        None
+    }
+
+    fn process_end_tag(&mut self) -> Result<(), Error> {
+        if let Some(el) = self.pop() {
+            if self.depth() > 0 {
+                let top = self.stack.len() - 1;
+                self.stack[top].append_child(el);
+            } else {
+                self.root = Some(el);
+            }
+        }
+
+        Ok(())
+    }
+
+    fn process_text(&mut self, text: String) {
+        if self.depth() > 0 {
+            let top = self.stack.len() - 1;
+            self.stack[top].append_text_node(text);
+        }
+    }
+
+    /// Process a Event that you got out of a RawParser
+    pub fn process_event(&mut self, event: RawEvent) -> Result<(), Error> {
+        match event {
+            RawEvent::XMLDeclaration(_, _) => {}
+
+            RawEvent::ElementHeadOpen(_, (prefix, name)) => {
+                self.next_tag = Some((
+                    prefix.map(|prefix| prefix.as_str().to_owned()),
+                    name.as_str().to_owned(),
+                    Prefixes::default(),
+                    BTreeMap::new(),
+                ))
+            }
+
+            RawEvent::Attribute(_, (prefix, name), value) => {
+                self.next_tag
+                    .as_mut()
+                    .map(
+                        |(_, _, ref mut prefixes, ref mut attrs)| match (prefix, name) {
+                            (None, xmlns) if xmlns == "xmlns" => {
+                                prefixes.insert(None, value);
+                            }
+                            (Some(xmlns), prefix) if xmlns.as_str() == "xmlns" => {
+                                prefixes.insert(Some(prefix.as_str().to_owned()), value);
+                            }
+                            (Some(prefix), name) => {
+                                attrs.insert(
+                                    format!("{}:{}", prefix, name),
+                                    value.as_str().to_owned(),
+                                );
+                            }
+                            (None, name) => {
+                                attrs.insert(name.as_str().to_owned(), value.as_str().to_owned());
+                            }
+                        },
+                    );
+            }
+
+            RawEvent::ElementHeadClose(_) => {
+                if let Some((prefix, name, prefixes, attrs)) = self.next_tag.take() {
+                    self.prefixes_stack.push(prefixes.clone());
+
+                    let namespace = self
+                        .lookup_prefix(&prefix.clone().map(|prefix| prefix.as_str().to_owned()))
+                        .ok_or(Error::MissingNamespace)?
+                        .to_owned();
+                    let el = Element::new(
+                        name.as_str().to_owned(),
+                        namespace,
+                        Some(prefix.map(|prefix| prefix.as_str().to_owned())),
+                        prefixes,
+                        attrs,
+                        vec![],
+                    );
+                    self.stack.push(el);
+                }
+            }
+
+            RawEvent::ElementFoot(_) => self.process_end_tag()?,
+
+            RawEvent::Text(_, text) => self.process_text(text.as_str().to_owned()),
+        }
+
+        Ok(())
+    }
+}

parsers/src/caps.rs πŸ”—

@@ -265,8 +265,7 @@ mod tests {
 
     #[test]
     fn test_xep_5_2() {
-        let elem: Element = r#"
-<query xmlns='http://jabber.org/protocol/disco#info'
+        let elem: Element = r#"<query xmlns='http://jabber.org/protocol/disco#info'
        node='http://psi-im.org#q07IKJEyjvHSyhy//CH0CxmKi8w='>
   <identity category='client' name='Exodus 0.9.1' type='pc'/>
   <feature var='http://jabber.org/protocol/caps'/>
@@ -294,8 +293,7 @@ mod tests {
 
     #[test]
     fn test_xep_5_3() {
-        let elem: Element = r#"
-<query xmlns='http://jabber.org/protocol/disco#info'
+        let elem: Element = r#"<query xmlns='http://jabber.org/protocol/disco#info'
        node='http://psi-im.org#q07IKJEyjvHSyhy//CH0CxmKi8w='>
   <identity xml:lang='en' category='client' name='Psi 0.11' type='pc'/>
   <identity xml:lang='el' category='client' name='Ξ¨ 0.11' type='pc'/>

parsers/src/cert_management.rs πŸ”—

@@ -176,8 +176,7 @@ mod tests {
 
     #[test]
     fn list() {
-        let elem: Element = r#"
-        <items xmlns='urn:xmpp:saslcert:1'>
+        let elem: Element = r#"<items xmlns='urn:xmpp:saslcert:1'>
           <item>
             <name>Mobile Client</name>
             <x509cert>AAAA</x509cert>

parsers/src/ecaps2.rs πŸ”—

@@ -236,8 +236,7 @@ mod tests {
 
     #[test]
     fn test_xep_ex1() {
-        let elem: Element = r#"
-<query xmlns="http://jabber.org/protocol/disco#info">
+        let elem: Element = r#"<query xmlns="http://jabber.org/protocol/disco#info">
   <identity category="client" name="BombusMod" type="mobile"/>
   <feature var="http://jabber.org/protocol/si"/>
   <feature var="http://jabber.org/protocol/bytestreams"/>
@@ -307,8 +306,7 @@ mod tests {
 
     #[test]
     fn test_xep_ex2() {
-        let elem: Element = r#"
-<query xmlns="http://jabber.org/protocol/disco#info">
+        let elem: Element = r#"<query xmlns="http://jabber.org/protocol/disco#info">
   <identity category="client" name="Tkabber" type="pc" xml:lang="en"/>
   <identity category="client" name="Π’ΠΊΠ°Π±Π±Π΅Ρ€" type="pc" xml:lang="ru"/>
   <feature var="games:board"/>

parsers/src/ibr.rs πŸ”—

@@ -137,8 +137,7 @@ mod tests {
 
     #[test]
     fn test_ex2() {
-        let elem: Element = r#"
-<query xmlns='jabber:iq:register'>
+        let elem: Element = r#"<query xmlns='jabber:iq:register'>
   <instructions>
     Choose a username and password for use with this service.
     Please also provide your email address.

parsers/src/jingle_ft.rs πŸ”—

@@ -354,8 +354,7 @@ mod tests {
 
     #[test]
     fn test_description() {
-        let elem: Element = r#"
-<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
+        let elem: Element = r#"<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
   <file>
     <media-type>text/plain</media-type>
     <name>test.txt</name>
@@ -387,8 +386,7 @@ mod tests {
 
     #[test]
     fn test_request() {
-        let elem: Element = r#"
-<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
+        let elem: Element = r#"<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
   <file>
     <hash xmlns='urn:xmpp:hashes:2'
           algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
@@ -413,8 +411,7 @@ mod tests {
 
     #[test]
     fn test_descs() {
-        let elem: Element = r#"
-<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
+        let elem: Element = r#"<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
   <file>
     <media-type>text/plain</media-type>
     <desc xml:lang='fr'>Fichier secretβ€―!</desc>
@@ -437,8 +434,7 @@ mod tests {
             Desc(String::from("Fichier secretβ€―!"))
         );
 
-        let elem: Element = r#"
-<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
+        let elem: Element = r#"<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
   <file>
     <media-type>text/plain</media-type>
     <desc xml:lang='fr'>Fichier secretβ€―!</desc>

parsers/src/jingle_grouping.rs πŸ”—

@@ -72,8 +72,7 @@ mod tests {
 
     #[test]
     fn parse_group() {
-        let elem: Element = "
-        <group xmlns='urn:xmpp:jingle:apps:grouping:0' semantics='BUNDLE'>
+        let elem: Element = "<group xmlns='urn:xmpp:jingle:apps:grouping:0' semantics='BUNDLE'>
             <content name='voice'/>
             <content name='webcam'/>
         </group>"

parsers/src/jingle_ice_udp.rs πŸ”—

@@ -134,8 +134,7 @@ mod tests {
 
     #[test]
     fn test_gajim() {
-        let elem: Element = "
-<transport xmlns='urn:xmpp:jingle:transports:ice-udp:1' pwd='wakMJ8Ydd5rqnPaFerws5o' ufrag='aeXX'>
+        let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ice-udp:1' pwd='wakMJ8Ydd5rqnPaFerws5o' ufrag='aeXX'>
     <candidate xmlns='urn:xmpp:jingle:transports:ice-udp:1' component='2' foundation='1' generation='0' id='11b72719-6a1b-4c51-8ae6-9f1538047568' ip='192.168.0.12' network='0' port='56715' priority='1010828030' protocol='tcp' type='host'/>
     <candidate xmlns='urn:xmpp:jingle:transports:ice-udp:1' component='2' foundation='1' generation='0' id='7e07b22d-db50-4e17-9ed9-eafeb96f4f63' ip='192.168.0.12' network='0' port='0' priority='1015022334' protocol='tcp' type='host'/>
     <candidate xmlns='urn:xmpp:jingle:transports:ice-udp:1' component='2' foundation='1' generation='0' id='431de362-c45f-40a8-bf10-9ed898a71d86' ip='192.168.0.12' network='0' port='36480' priority='2013266428' protocol='udp' type='host'/>
@@ -164,8 +163,7 @@ mod tests {
 
     #[test]
     fn test_jitsi_meet() {
-        let elem: Element = "
-<transport ufrag='2acq51d4p07v2m' pwd='7lk9uul39gckit6t02oavv2r9j' xmlns='urn:xmpp:jingle:transports:ice-udp:1'>
+        let elem: Element = "<transport ufrag='2acq51d4p07v2m' pwd='7lk9uul39gckit6t02oavv2r9j' xmlns='urn:xmpp:jingle:transports:ice-udp:1'>
     <fingerprint hash='sha-1' setup='actpass' xmlns='urn:xmpp:jingle:apps:dtls:0'>97:F2:B5:BE:DB:A6:00:B1:3E:40:B2:41:3C:0D:FC:E0:BD:B2:A0:E8</fingerprint>
     <candidate type='host' protocol='udp' id='186cb069513c2bbe546192c93cc4ab3b05ab0d426' ip='2a05:d014:fc7:54a1:8bfc:7248:3d1c:51a4' component='1' port='10000' foundation='1' generation='0' priority='2130706431' network='0'/>
     <candidate type='host' protocol='udp' id='186cb069513c2bbe546192c93cc4ab3b063daeefd' ip='10.15.1.120' component='1' port='10000' foundation='2' generation='0' priority='2130706431' network='0'/>

parsers/src/jingle_raw_udp.rs πŸ”—

@@ -78,8 +78,7 @@ mod tests {
 
     #[test]
     fn example_1() {
-        let elem: Element = "
-<transport xmlns='urn:xmpp:jingle:transports:raw-udp:1'>
+        let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:raw-udp:1'>
     <candidate component='1'
                generation='0'
                id='a9j3mnbtu1'

parsers/src/jingle_rtp.rs πŸ”—

@@ -175,8 +175,7 @@ mod tests {
 
     #[test]
     fn test_simple() {
-        let elem: Element = "
-<description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>
+        let elem: Element = "<description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>
     <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='2' clockrate='48000' id='96' name='OPUS'/>
     <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='32000' id='105' name='SPEEX'/>
     <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='8000' id='9' name='G722'/>

parsers/src/jingle_rtp_hdrext.rs πŸ”—

@@ -74,8 +74,7 @@ mod tests {
 
     #[test]
     fn parse_exthdr() {
-        let elem: Element = "
-        <rtp-hdrext xmlns='urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'
+        let elem: Element = "<rtp-hdrext xmlns='urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'
                     uri='urn:ietf:params:rtp-hdrext:toffset'
                     id='1'/>"
             .parse()

parsers/src/jingle_ssma.rs πŸ”—

@@ -101,8 +101,7 @@ mod tests {
 
     #[test]
     fn parse_source() {
-        let elem: Element = "
-<source ssrc='1656081975' xmlns='urn:xmpp:jingle:apps:rtp:ssma:0'>
+        let elem: Element = "<source ssrc='1656081975' xmlns='urn:xmpp:jingle:apps:rtp:ssma:0'>
     <parameter name='cname' value='Yv/wvbCdsDW2Prgd'/>
     <parameter name='msid' value='MLTJKIHilGn71fNQoszkQ4jlPTuS5vJyKVIv MLTJKIHilGn71fNQoszkQ4jlPTuS5vJyKVIva0'/>
 </source>"
@@ -124,8 +123,7 @@ mod tests {
 
     #[test]
     fn parse_source_group() {
-        let elem: Element = "
-<ssrc-group semantics='FID' xmlns='urn:xmpp:jingle:apps:rtp:ssma:0'>
+        let elem: Element = "<ssrc-group semantics='FID' xmlns='urn:xmpp:jingle:apps:rtp:ssma:0'>
     <source ssrc='2301230316'/>
     <source ssrc='386328120'/>
 </ssrc-group>"

parsers/src/legacy_omemo.rs πŸ”—

@@ -186,8 +186,7 @@ mod tests {
 
     #[test]
     fn parse_bundle() {
-        let elem: Element = r#"
-<bundle xmlns="eu.siacs.conversations.axolotl">
+        let elem: Element = r#"<bundle xmlns="eu.siacs.conversations.axolotl">
   <signedPreKeyPublic signedPreKeyId="1">BYAbACA15bPn95p7RGC2XbgQyly8aRKS4BaJ+hD8Ybhe</signedPreKeyPublic>
   <signedPreKeySignature>sIJVNDZi/NgFsry4OBdM+adyGttLEXbUh/h/5dVOZveMgyVoIdgwBUzq8Wgd2xYTQMioNzwYebTX+9p0h9eujA==</signedPreKeySignature>
   <identityKey>BQFd2p/Oq97vAAdLKA09DlcSg0x1xWn260p1jaeyIhAZ</identityKey>
@@ -254,8 +253,7 @@ mod tests {
     }
     #[test]
     fn parse_device_list() {
-        let elem: Element = r#"
-<list xmlns="eu.siacs.conversations.axolotl">
+        let elem: Element = r#"<list xmlns="eu.siacs.conversations.axolotl">
   <device id="1164059891" />
   <device id="26052318" />
   <device id="564866972" />
@@ -275,8 +273,7 @@ mod tests {
     }
     #[test]
     fn parse_encrypted() {
-        let elem: Element = r#"
-<encrypted xmlns="eu.siacs.conversations.axolotl">
+        let elem: Element = r#"<encrypted xmlns="eu.siacs.conversations.axolotl">
   <header sid="564866972">
     <key prekey="true" rid="1236">Mwjp9AESIQVylscLPpj/HlowaTiIsaBj73HCVEllXpVTtMG9EYwRexohBQFd2p/Oq97vAAdLKA09DlcSg0x1xWn260p1jaeyIhAZImIzCiEFhaQ4I+DuQgo6vCLCjHu4uewDZmWHuBl8uJw1IkyZxhUQABgAIjCoEVgVThWlaIlnN3V5Bg1hQX7OD1cvstLD5lH3zZMadL3KeONELESlBbeKmNgcYC/e3HZnbgWzBiic36yNAjAW</key>
     <key rid="26052318">MwohBTV6dpumL1OxA9MdIFmu2E19+cIWDHWYfhdubvo0hmh6EAAYHCIwNc9/59eeYi8pVZQhMJJMVkKUkFP/yrTfG3o1lfpHGseCqb/JTgtDytQPiYrTpHl2V/mdsM6IPig=</key>

parsers/src/mam.rs πŸ”—

@@ -123,8 +123,7 @@ mod tests {
     #[test]
     fn test_result() {
         #[cfg(not(feature = "component"))]
-        let elem: Element = r#"
-<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
+        let elem: Element = r#"<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
   <forwarded xmlns='urn:xmpp:forward:0'>
     <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>
     <message xmlns='jabber:client' from="witch@shakespeare.lit" to="macbeth@shakespeare.lit">
@@ -136,8 +135,7 @@ mod tests {
         .parse()
         .unwrap();
         #[cfg(feature = "component")]
-        let elem: Element = r#"
-<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
+        let elem: Element = r#"<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
   <forwarded xmlns='urn:xmpp:forward:0'>
     <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>
     <message xmlns='jabber:component:accept' from="witch@shakespeare.lit" to="macbeth@shakespeare.lit">
@@ -151,8 +149,7 @@ mod tests {
 
     #[test]
     fn test_fin() {
-        let elem: Element = r#"
-<fin xmlns='urn:xmpp:mam:2'>
+        let elem: Element = r#"<fin xmlns='urn:xmpp:mam:2'>
   <set xmlns='http://jabber.org/protocol/rsm'>
     <first index='0'>28482-98726-73623</first>
     <last>09af3-cc343-b409f</last>
@@ -166,8 +163,7 @@ mod tests {
 
     #[test]
     fn test_query_x() {
-        let elem: Element = r#"
-<query xmlns='urn:xmpp:mam:2'>
+        let elem: Element = r#"<query xmlns='urn:xmpp:mam:2'>
   <x xmlns='jabber:x:data' type='submit'>
     <field var='FORM_TYPE' type='hidden'>
       <value>urn:xmpp:mam:2</value>
@@ -185,8 +181,7 @@ mod tests {
 
     #[test]
     fn test_query_x_set() {
-        let elem: Element = r#"
-<query xmlns='urn:xmpp:mam:2'>
+        let elem: Element = r#"<query xmlns='urn:xmpp:mam:2'>
   <x xmlns='jabber:x:data' type='submit'>
     <field var='FORM_TYPE' type='hidden'>
       <value>urn:xmpp:mam:2</value>

parsers/src/mam_prefs.rs πŸ”—

@@ -134,8 +134,7 @@ mod tests {
         assert!(prefs.always.is_empty());
         assert!(prefs.never.is_empty());
 
-        let elem: Element = r#"
-<prefs xmlns='urn:xmpp:mam:2' default='roster'>
+        let elem: Element = r#"<prefs xmlns='urn:xmpp:mam:2' default='roster'>
   <always/>
   <never/>
 </prefs>
@@ -149,8 +148,7 @@ mod tests {
 
     #[test]
     fn test_prefs_result() {
-        let elem: Element = r#"
-<prefs xmlns='urn:xmpp:mam:2' default='roster'>
+        let elem: Element = r#"<prefs xmlns='urn:xmpp:mam:2' default='roster'>
   <always>
     <jid>romeo@montague.lit</jid>
   </always>

parsers/src/media_element.rs πŸ”—

@@ -174,8 +174,7 @@ mod tests {
 
     #[test]
     fn test_xep_ex1() {
-        let elem: Element = r#"
-<media xmlns='urn:xmpp:media-element'>
+        let elem: Element = r#"<media xmlns='urn:xmpp:media-element'>
   <uri type='audio/x-wav'>
     http://victim.example.com/challenges/speech.wav?F3A6292C
   </uri>
@@ -211,8 +210,7 @@ mod tests {
 
     #[test]
     fn test_xep_ex2() {
-        let elem: Element = r#"
-<x xmlns='jabber:x:data' type='form'>
+        let elem: Element = r#"<x xmlns='jabber:x:data' type='form'>
   [ ... ]
   <field var='ocr'>
     <media xmlns='urn:xmpp:media-element'

parsers/src/muc/muc.rs πŸ”—

@@ -163,8 +163,7 @@ mod tests {
 
     #[test]
     fn history() {
-        let elem: Element = "
-            <x xmlns='http://jabber.org/protocol/muc'>
+        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'>
                 <history maxstanzas='0'/>
             </x>"
             .parse()
@@ -179,8 +178,7 @@ mod tests {
         assert_eq!(history.seconds, None);
         assert_eq!(history.since, None);
 
-        let elem: Element = "
-            <x xmlns='http://jabber.org/protocol/muc'>
+        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'>
                 <history since='1970-01-01T00:00:00Z'/>
             </x>"
             .parse()

parsers/src/muc/user.rs πŸ”—

@@ -239,25 +239,21 @@ mod tests {
 
     #[test]
     fn test_simple() {
-        let elem: Element = "
-            <x xmlns='http://jabber.org/protocol/muc#user'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>"
+            .parse()
+            .unwrap();
         MucUser::try_from(elem).unwrap();
     }
 
     #[test]
     fn statuses_and_items() {
-        let elem: Element = "
-            <x xmlns='http://jabber.org/protocol/muc#user'>
+        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'>
                 <status code='101'/>
                 <status code='102'/>
                 <item affiliation='member' role='moderator'/>
-            </x>
-        "
-        .parse()
-        .unwrap();
+            </x>"
+            .parse()
+            .unwrap();
         let muc_user = MucUser::try_from(elem).unwrap();
         assert_eq!(muc_user.status.len(), 2);
         assert_eq!(muc_user.status[0], Status::AffiliationChange);
@@ -269,13 +265,11 @@ mod tests {
 
     #[test]
     fn test_invalid_child() {
-        let elem: Element = "
-            <x xmlns='http://jabber.org/protocol/muc#user'>
+        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'>
                 <coucou/>
-            </x>
-        "
-        .parse()
-        .unwrap();
+            </x>"
+            .parse()
+            .unwrap();
         let error = MucUser::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -286,11 +280,9 @@ mod tests {
 
     #[test]
     fn test_serialise() {
-        let elem: Element = "
-            <x xmlns='http://jabber.org/protocol/muc#user'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>"
+            .parse()
+            .unwrap();
         let muc = MucUser {
             status: vec![],
             items: vec![],
@@ -302,11 +294,9 @@ mod tests {
     #[cfg(not(feature = "disable-validation"))]
     #[test]
     fn test_invalid_attribute() {
-        let elem: Element = "
-            <x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>"
+            .parse()
+            .unwrap();
         let error = MucUser::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -317,21 +307,17 @@ mod tests {
 
     #[test]
     fn test_status_simple() {
-        let elem: Element = "
-            <status xmlns='http://jabber.org/protocol/muc#user' code='110'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>"
+            .parse()
+            .unwrap();
         Status::try_from(elem).unwrap();
     }
 
     #[test]
     fn test_status_invalid() {
-        let elem: Element = "
-            <status xmlns='http://jabber.org/protocol/muc#user'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user'/>"
+            .parse()
+            .unwrap();
         let error = Status::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -343,13 +329,11 @@ mod tests {
     #[cfg(not(feature = "disable-validation"))]
     #[test]
     fn test_status_invalid_child() {
-        let elem: Element = "
-            <status xmlns='http://jabber.org/protocol/muc#user' code='110'>
+        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'>
                 <foo/>
-            </status>
-        "
-        .parse()
-        .unwrap();
+            </status>"
+            .parse()
+            .unwrap();
         let error = Status::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -360,22 +344,18 @@ mod tests {
 
     #[test]
     fn test_status_simple_code() {
-        let elem: Element = "
-            <status xmlns='http://jabber.org/protocol/muc#user' code='307'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='307'/>"
+            .parse()
+            .unwrap();
         let status = Status::try_from(elem).unwrap();
         assert_eq!(status, Status::Kicked);
     }
 
     #[test]
     fn test_status_invalid_code() {
-        let elem: Element = "
-            <status xmlns='http://jabber.org/protocol/muc#user' code='666'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='666'/>"
+            .parse()
+            .unwrap();
         let error = Status::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -386,11 +366,9 @@ mod tests {
 
     #[test]
     fn test_status_invalid_code2() {
-        let elem: Element = "
-            <status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>"
+            .parse()
+            .unwrap();
         let error = Status::try_from(elem).unwrap_err();
         let error = match error {
             Error::ParseIntError(error) => error,
@@ -401,11 +379,9 @@ mod tests {
 
     #[test]
     fn test_actor_required_attributes() {
-        let elem: Element = "
-            <actor xmlns='http://jabber.org/protocol/muc#user'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user'/>"
+            .parse()
+            .unwrap();
         let error = Actor::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -416,13 +392,11 @@ mod tests {
 
     #[test]
     fn test_actor_required_attributes2() {
-        let elem: Element = "
-            <actor xmlns='http://jabber.org/protocol/muc#user'
+        let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user'
                    jid='foo@bar/baz'
-                   nick='baz'/>
-        "
-        .parse()
-        .unwrap();
+                   nick='baz'/>"
+            .parse()
+            .unwrap();
         let error = Actor::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -433,12 +407,10 @@ mod tests {
 
     #[test]
     fn test_actor_jid() {
-        let elem: Element = "
-            <actor xmlns='http://jabber.org/protocol/muc#user'
-                   jid='foo@bar/baz'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user'
+                   jid='foo@bar/baz'/>"
+            .parse()
+            .unwrap();
         let actor = Actor::try_from(elem).unwrap();
         let jid = match actor {
             Actor::Jid(jid) => jid,
@@ -449,11 +421,9 @@ mod tests {
 
     #[test]
     fn test_actor_nick() {
-        let elem: Element = "
-            <actor xmlns='http://jabber.org/protocol/muc#user' nick='baz'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<actor xmlns='http://jabber.org/protocol/muc#user' nick='baz'/>"
+            .parse()
+            .unwrap();
         let actor = Actor::try_from(elem).unwrap();
         let nick = match actor {
             Actor::Nick(nick) => nick,
@@ -464,35 +434,29 @@ mod tests {
 
     #[test]
     fn test_continue_simple() {
-        let elem: Element = "
-            <continue xmlns='http://jabber.org/protocol/muc#user'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<continue xmlns='http://jabber.org/protocol/muc#user'/>"
+            .parse()
+            .unwrap();
         Continue::try_from(elem).unwrap();
     }
 
     #[test]
     fn test_continue_thread_attribute() {
-        let elem: Element = "
-            <continue xmlns='http://jabber.org/protocol/muc#user'
-                      thread='foo'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<continue xmlns='http://jabber.org/protocol/muc#user'
+                      thread='foo'/>"
+            .parse()
+            .unwrap();
         let continue_ = Continue::try_from(elem).unwrap();
         assert_eq!(continue_.thread, Some("foo".to_owned()));
     }
 
     #[test]
     fn test_continue_invalid() {
-        let elem: Element = "
-            <continue xmlns='http://jabber.org/protocol/muc#user'>
+        let elem: Element = "<continue xmlns='http://jabber.org/protocol/muc#user'>
                 <foobar/>
-            </continue>
-        "
-        .parse()
-        .unwrap();
+            </continue>"
+            .parse()
+            .unwrap();
         let continue_ = Continue::try_from(elem).unwrap_err();
         let message = match continue_ {
             Error::ParseError(string) => string,
@@ -503,8 +467,7 @@ mod tests {
 
     #[test]
     fn test_reason_simple() {
-        let elem: Element = "
-            <reason xmlns='http://jabber.org/protocol/muc#user'>Reason</reason>"
+        let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user'>Reason</reason>"
             .parse()
             .unwrap();
         let elem2 = elem.clone();
@@ -518,11 +481,9 @@ mod tests {
     #[cfg(not(feature = "disable-validation"))]
     #[test]
     fn test_reason_invalid_attribute() {
-        let elem: Element = "
-            <reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>"
+            .parse()
+            .unwrap();
         let error = Reason::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -534,13 +495,11 @@ mod tests {
     #[cfg(not(feature = "disable-validation"))]
     #[test]
     fn test_reason_invalid() {
-        let elem: Element = "
-            <reason xmlns='http://jabber.org/protocol/muc#user'>
+        let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user'>
                 <foobar/>
-            </reason>
-        "
-        .parse()
-        .unwrap();
+            </reason>"
+            .parse()
+            .unwrap();
         let error = Reason::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -552,12 +511,10 @@ mod tests {
     #[cfg(not(feature = "disable-validation"))]
     #[test]
     fn test_item_invalid_attr() {
-        let elem: Element = "
-            <item xmlns='http://jabber.org/protocol/muc#user'
-                  foo='bar'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
+                  foo='bar'/>"
+            .parse()
+            .unwrap();
         let error = Item::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -568,24 +525,20 @@ mod tests {
 
     #[test]
     fn test_item_affiliation_role_attr() {
-        let elem: Element = "
-            <item xmlns='http://jabber.org/protocol/muc#user'
+        let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
                   affiliation='member'
-                  role='moderator'/>
-        "
-        .parse()
-        .unwrap();
+                  role='moderator'/>"
+            .parse()
+            .unwrap();
         Item::try_from(elem).unwrap();
     }
 
     #[test]
     fn test_item_affiliation_role_invalid_attr() {
-        let elem: Element = "
-            <item xmlns='http://jabber.org/protocol/muc#user'
-                  affiliation='member'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
+                  affiliation='member'/>"
+            .parse()
+            .unwrap();
         let error = Item::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -596,14 +549,12 @@ mod tests {
 
     #[test]
     fn test_item_nick_attr() {
-        let elem: Element = "
-            <item xmlns='http://jabber.org/protocol/muc#user'
+        let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
                   affiliation='member'
                   role='moderator'
-                  nick='foobar'/>
-        "
-        .parse()
-        .unwrap();
+                  nick='foobar'/>"
+            .parse()
+            .unwrap();
         let item = Item::try_from(elem).unwrap();
         match item {
             Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())),
@@ -612,12 +563,10 @@ mod tests {
 
     #[test]
     fn test_item_affiliation_role_invalid_attr2() {
-        let elem: Element = "
-            <item xmlns='http://jabber.org/protocol/muc#user'
-                  role='moderator'/>
-        "
-        .parse()
-        .unwrap();
+        let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
+                  role='moderator'/>"
+            .parse()
+            .unwrap();
         let error = Item::try_from(elem).unwrap_err();
         let message = match error {
             Error::ParseError(string) => string,
@@ -631,15 +580,13 @@ mod tests {
 
     #[test]
     fn test_item_role_actor_child() {
-        let elem: Element = "
-            <item xmlns='http://jabber.org/protocol/muc#user'
+        let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
                   affiliation='member'
                   role='moderator'>
                 <actor nick='foobar'/>
-            </item>
-        "
-        .parse()
-        .unwrap();
+            </item>"
+            .parse()
+            .unwrap();
         let item = Item::try_from(elem).unwrap();
         match item {
             Item { actor, .. } => assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
@@ -648,15 +595,13 @@ mod tests {
 
     #[test]
     fn test_item_role_continue_child() {
-        let elem: Element = "
-            <item xmlns='http://jabber.org/protocol/muc#user'
+        let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
                   affiliation='member'
                   role='moderator'>
                 <continue thread='foobar'/>
-            </item>
-        "
-        .parse()
-        .unwrap();
+            </item>"
+            .parse()
+            .unwrap();
         let item = Item::try_from(elem).unwrap();
         let continue_1 = Continue {
             thread: Some("foobar".to_owned()),
@@ -672,15 +617,13 @@ mod tests {
 
     #[test]
     fn test_item_role_reason_child() {
-        let elem: Element = "
-            <item xmlns='http://jabber.org/protocol/muc#user'
+        let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
                   affiliation='member'
                   role='moderator'>
                 <reason>foobar</reason>
-            </item>
-        "
-        .parse()
-        .unwrap();
+            </item>"
+            .parse()
+            .unwrap();
         let item = Item::try_from(elem).unwrap();
         match item {
             Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))),

parsers/src/roster.rs πŸ”—

@@ -142,8 +142,7 @@ mod tests {
         assert_eq!(roster.ver, Some(String::from("ver9")));
         assert!(roster.items.is_empty());
 
-        let elem: Element = r#"
-<query xmlns='jabber:iq:roster' ver='ver11'>
+        let elem: Element = r#"<query xmlns='jabber:iq:roster' ver='ver11'>
   <item jid='romeo@example.net'
         name='Romeo'
         subscription='both'>
@@ -215,16 +214,14 @@ mod tests {
         assert!(roster.ver.is_none());
         assert_eq!(roster.items.len(), 1);
 
-        let elem: Element = r#"
-<query xmlns='jabber:iq:roster'>
+        let elem: Element = r#"<query xmlns='jabber:iq:roster'>
   <item jid='nurse@example.com'
         name='Nurse'>
     <group>Servants</group>
   </item>
-</query>
-"#
-        .parse()
-        .unwrap();
+</query>"#
+            .parse()
+            .unwrap();
         let roster = Roster::try_from(elem).unwrap();
         assert!(roster.ver.is_none());
         assert_eq!(roster.items.len(), 1);
@@ -236,14 +233,12 @@ mod tests {
             Group::from_str("Servants").unwrap()
         );
 
-        let elem: Element = r#"
-<query xmlns='jabber:iq:roster'>
+        let elem: Element = r#"<query xmlns='jabber:iq:roster'>
   <item jid='nurse@example.com'
         subscription='remove'/>
-</query>
-"#
-        .parse()
-        .unwrap();
+</query>"#
+            .parse()
+            .unwrap();
         let roster = Roster::try_from(elem).unwrap();
         assert!(roster.ver.is_none());
         assert_eq!(roster.items.len(), 1);

parsers/src/sm.rs πŸ”—

@@ -180,7 +180,7 @@ mod tests {
 
     #[test]
     fn a() {
-        let elem: Element = "<a xmlns='urn:xmpp:sm:3' h='5'".parse().unwrap();
+        let elem: Element = "<a xmlns='urn:xmpp:sm:3' h='5'/>".parse().unwrap();
         let a = A::try_from(elem).unwrap();
         assert_eq!(a.h, 5);
     }

parsers/src/time.rs πŸ”—

@@ -46,7 +46,7 @@ impl TryFrom<Element> for TimeResult {
                 check_no_children!(child, "tzo");
                 check_no_attributes!(child, "tzo");
                 // TODO: Add a FromStr implementation to FixedOffset to avoid this hack.
-                let fake_date = String::from("2019-04-22T11:38:00") + &child.text();
+                let fake_date = format!("{}{}", "2019-04-22T11:38:00", child.text());
                 let date_time = DateTime::from_str(&fake_date)?;
                 tzo = Some(date_time.timezone());
             } else if child.is("utc", ns::TIME) {

tokio-xmpp/Cargo.toml πŸ”—

@@ -25,8 +25,9 @@ tokio-stream = { version = "0.1", features = [] }
 tokio-util = { version = "0.6", features = ["codec"] }
 trust-dns-proto = "0.20"
 trust-dns-resolver = "0.20"
-xml5ever = "0.16"
 xmpp-parsers = "0.19"
+minidom = "0.14"
+rxml = "^0.7.1"
 webpki-roots = { version = "0.22", optional = true }
 
 [build-dependencies]

tokio-xmpp/src/client/async_client.rs πŸ”—

@@ -203,30 +203,28 @@ impl Stream for Client {
                 self.poll_next(cx)
             }
             ClientState::Disconnected => Poll::Ready(None),
-            ClientState::Connecting(mut connect) => {
-                match Pin::new(&mut connect).poll(cx) {
-                    Poll::Ready(Ok(Ok(stream))) => {
-                        let bound_jid = stream.jid.clone();
-                        self.state = ClientState::Connected(stream);
-                        Poll::Ready(Some(Event::Online {
-                            bound_jid,
-                            resumed: false,
-                        }))
-                    }
-                    Poll::Ready(Ok(Err(e))) => {
-                        self.state = ClientState::Disconnected;
-                        return Poll::Ready(Some(Event::Disconnected(e.into())));
-                    }
-                    Poll::Ready(Err(e)) => {
-                        self.state = ClientState::Disconnected;
-                        panic!("connect task: {}", e);
-                    }
-                    Poll::Pending => {
-                        self.state = ClientState::Connecting(connect);
-                        Poll::Pending
-                    }
+            ClientState::Connecting(mut connect) => match Pin::new(&mut connect).poll(cx) {
+                Poll::Ready(Ok(Ok(stream))) => {
+                    let bound_jid = stream.jid.clone();
+                    self.state = ClientState::Connected(stream);
+                    Poll::Ready(Some(Event::Online {
+                        bound_jid,
+                        resumed: false,
+                    }))
                 }
-            }
+                Poll::Ready(Ok(Err(e))) => {
+                    self.state = ClientState::Disconnected;
+                    return Poll::Ready(Some(Event::Disconnected(e.into())));
+                }
+                Poll::Ready(Err(e)) => {
+                    self.state = ClientState::Disconnected;
+                    panic!("connect task: {}", e);
+                }
+                Poll::Pending => {
+                    self.state = ClientState::Connecting(connect);
+                    Poll::Pending
+                }
+            },
             ClientState::Connected(mut stream) => {
                 // Poll sink
                 match Pin::new(&mut stream).poll_ready(cx) {

tokio-xmpp/src/error.rs πŸ”—

@@ -5,7 +5,6 @@ use std::borrow::Cow;
 use std::error::Error as StdError;
 use std::fmt;
 use std::io::Error as IoError;
-use std::str::Utf8Error;
 #[cfg(feature = "tls-rust")]
 use tokio_rustls::rustls::client::InvalidDnsNameError;
 #[cfg(feature = "tls-rust")]
@@ -106,44 +105,6 @@ impl From<InvalidDnsNameError> for Error {
     }
 }
 
-/// Causes for stream parsing errors
-#[derive(Debug)]
-pub enum ParserError {
-    /// Encoding error
-    Utf8(Utf8Error),
-    /// XML parse error
-    Parse(ParseError),
-    /// Illegal `</>`
-    ShortTag,
-    /// Required by `impl Decoder`
-    Io(IoError),
-}
-
-impl fmt::Display for ParserError {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            ParserError::Utf8(e) => write!(fmt, "UTF-8 error: {}", e),
-            ParserError::Parse(e) => write!(fmt, "parse error: {}", e),
-            ParserError::ShortTag => write!(fmt, "short tag"),
-            ParserError::Io(e) => write!(fmt, "IO error: {}", e),
-        }
-    }
-}
-
-impl StdError for ParserError {}
-
-impl From<IoError> for ParserError {
-    fn from(e: IoError) -> Self {
-        ParserError::Io(e)
-    }
-}
-
-impl From<ParserError> for Error {
-    fn from(e: ParserError) -> Self {
-        ProtocolError::Parser(e).into()
-    }
-}
-
 /// XML parse error wrapper type
 #[derive(Debug)]
 pub struct ParseError(pub Cow<'static, str>);
@@ -167,7 +128,7 @@ impl fmt::Display for ParseError {
 #[derive(Debug)]
 pub enum ProtocolError {
     /// XML parser error
-    Parser(ParserError),
+    Parser(minidom::Error),
     /// Error with expected stanza schema
     Parsers(ParsersError),
     /// No TLS available
@@ -205,12 +166,18 @@ impl fmt::Display for ProtocolError {
 
 impl StdError for ProtocolError {}
 
-impl From<ParserError> for ProtocolError {
-    fn from(e: ParserError) -> Self {
+impl From<minidom::Error> for ProtocolError {
+    fn from(e: minidom::Error) -> Self {
         ProtocolError::Parser(e)
     }
 }
 
+impl From<minidom::Error> for Error {
+    fn from(e: minidom::Error) -> Self {
+        ProtocolError::Parser(e).into()
+    }
+}
+
 impl From<ParsersError> for ProtocolError {
     fn from(e: ParsersError) -> Self {
         ProtocolError::Parsers(e)

tokio-xmpp/src/lib.rs πŸ”—

@@ -16,5 +16,5 @@ pub use client::{async_client::Client as AsyncClient, simple_client::Client as S
 mod component;
 pub use crate::component::Component;
 mod error;
-pub use crate::error::{AuthError, ConnecterError, Error, ParseError, ParserError, ProtocolError};
+pub use crate::error::{AuthError, ConnecterError, Error, ParseError, ProtocolError};
 pub use starttls::starttls;

tokio-xmpp/src/xmpp_codec.rs πŸ”—

@@ -1,23 +1,16 @@
 //! XML stream parser for XMPP
 
-use crate::{ParseError, ParserError};
+use crate::Error;
 use bytes::{BufMut, BytesMut};
-use log::{debug, error};
+use log::debug;
+use minidom::tree_builder::TreeBuilder;
+use rxml::{Lexer, PushDriver, RawParser};
 use std;
-use std::borrow::Cow;
-use std::collections::vec_deque::VecDeque;
 use std::collections::HashMap;
 use std::default::Default;
 use std::fmt::Write;
 use std::io;
-use std::iter::FromIterator;
-use std::str::from_utf8;
-use std::sync::Arc;
-use std::sync::Mutex;
 use tokio_util::codec::{Decoder, Encoder};
-use xml5ever::buffer_queue::BufferQueue;
-use xml5ever::interface::Attribute;
-use xml5ever::tokenizer::{Tag, TagKind, Token, TokenSink, XmlTokenizer};
 use xmpp_parsers::Element;
 
 /// Anything that can be sent or received on an XMPP/XML stream
@@ -33,175 +26,24 @@ pub enum Packet {
     StreamEnd,
 }
 
-type QueueItem = Result<Packet, ParserError>;
-
-/// Parser state
-struct ParserSink {
-    // Ready stanzas, shared with XMPPCodec
-    queue: Arc<Mutex<VecDeque<QueueItem>>>,
-    // Parsing stack
-    stack: Vec<Element>,
-    ns_stack: Vec<HashMap<Option<String>, String>>,
-}
-
-impl ParserSink {
-    pub fn new(queue: Arc<Mutex<VecDeque<QueueItem>>>) -> Self {
-        ParserSink {
-            queue,
-            stack: vec![],
-            ns_stack: vec![],
-        }
-    }
-
-    fn push_queue(&self, pkt: Packet) {
-        self.queue.lock().unwrap().push_back(Ok(pkt));
-    }
-
-    fn push_queue_error(&self, e: ParserError) {
-        self.queue.lock().unwrap().push_back(Err(e));
-    }
-
-    /// Lookup XML namespace declaration for given prefix (or no prefix)
-    fn lookup_ns(&self, prefix: &Option<String>) -> Option<&str> {
-        for nss in self.ns_stack.iter().rev() {
-            if let Some(ns) = nss.get(prefix) {
-                return Some(ns);
-            }
-        }
-
-        None
-    }
-
-    fn handle_start_tag(&mut self, tag: Tag) {
-        let mut nss = HashMap::new();
-        let is_prefix_xmlns = |attr: &Attribute| {
-            attr.name
-                .prefix
-                .as_ref()
-                .map(|prefix| prefix.eq_str_ignore_ascii_case("xmlns"))
-                .unwrap_or(false)
-        };
-        for attr in &tag.attrs {
-            match attr.name.local.as_ref() {
-                "xmlns" => {
-                    nss.insert(None, attr.value.as_ref().to_owned());
-                }
-                prefix if is_prefix_xmlns(attr) => {
-                    nss.insert(Some(prefix.to_owned()), attr.value.as_ref().to_owned());
-                }
-                _ => (),
-            }
-        }
-        self.ns_stack.push(nss);
-
-        let el = {
-            let el_ns = self
-                .lookup_ns(&tag.name.prefix.map(|prefix| prefix.as_ref().to_owned()))
-                .unwrap();
-            let mut el_builder = Element::builder(tag.name.local.as_ref(), el_ns);
-            for attr in &tag.attrs {
-                match attr.name.local.as_ref() {
-                    "xmlns" => (),
-                    _ if is_prefix_xmlns(attr) => (),
-                    _ => {
-                        let attr_name = if let Some(ref prefix) = attr.name.prefix {
-                            Cow::Owned(format!("{}:{}", prefix, attr.name.local))
-                        } else {
-                            Cow::Borrowed(attr.name.local.as_ref())
-                        };
-                        el_builder = el_builder.attr(attr_name, attr.value.as_ref());
-                    }
-                }
-            }
-            el_builder.build()
-        };
-
-        if self.stack.is_empty() {
-            let attrs = HashMap::from_iter(tag.attrs.iter().map(|attr| {
-                (
-                    attr.name.local.as_ref().to_owned(),
-                    attr.value.as_ref().to_owned(),
-                )
-            }));
-            self.push_queue(Packet::StreamStart(attrs));
-        }
-
-        self.stack.push(el);
-    }
-
-    fn handle_end_tag(&mut self) {
-        let el = self.stack.pop().unwrap();
-        self.ns_stack.pop();
-
-        match self.stack.len() {
-            // </stream:stream>
-            0 => self.push_queue(Packet::StreamEnd),
-            // </stanza>
-            1 => self.push_queue(Packet::Stanza(el)),
-            len => {
-                let parent = &mut self.stack[len - 1];
-                parent.append_child(el);
-            }
-        }
-    }
-}
-
-impl TokenSink for ParserSink {
-    fn process_token(&mut self, token: Token) {
-        match token {
-            Token::TagToken(tag) => match tag.kind {
-                TagKind::StartTag => self.handle_start_tag(tag),
-                TagKind::EndTag => self.handle_end_tag(),
-                TagKind::EmptyTag => {
-                    self.handle_start_tag(tag);
-                    self.handle_end_tag();
-                }
-                TagKind::ShortTag => self.push_queue_error(ParserError::ShortTag),
-            },
-            Token::CharacterTokens(tendril) => match self.stack.len() {
-                0 | 1 => self.push_queue(Packet::Text(tendril.into())),
-                len => {
-                    let el = &mut self.stack[len - 1];
-                    el.append_text_node(tendril);
-                }
-            },
-            Token::EOFToken => self.push_queue(Packet::StreamEnd),
-            Token::ParseError(s) => {
-                self.push_queue_error(ParserError::Parse(ParseError(s)));
-            }
-            _ => (),
-        }
-    }
-
-    // fn end(&mut self) {
-    // }
-}
-
 /// Stateful encoder/decoder for a bytestream from/to XMPP `Packet`
 pub struct XMPPCodec {
     /// Outgoing
     ns: Option<String>,
     /// Incoming
-    parser: XmlTokenizer<ParserSink>,
-    /// For handling incoming truncated utf8
-    // TODO: optimize using  tendrils?
-    buf: Vec<u8>,
-    /// Shared with ParserSink
-    queue: Arc<Mutex<VecDeque<QueueItem>>>,
+    driver: PushDriver<RawParser>,
+    stanza_builder: TreeBuilder,
 }
 
 impl XMPPCodec {
     /// Constructor
     pub fn new() -> Self {
-        let queue = Arc::new(Mutex::new(VecDeque::new()));
-        let sink = ParserSink::new(queue.clone());
-        // TODO: configure parser?
-        let parser = XmlTokenizer::new(sink, Default::default());
+        let stanza_builder = TreeBuilder::new();
+        let driver = PushDriver::wrap(Lexer::new(), RawParser::new());
         XMPPCodec {
             ns: None,
-            parser,
-            queue,
-            buf: vec![],
+            driver,
+            stanza_builder,
         }
     }
 }
@@ -214,57 +56,53 @@ impl Default for XMPPCodec {
 
 impl Decoder for XMPPCodec {
     type Item = Packet;
-    type Error = ParserError;
+    type Error = Error;
 
     fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
-        let buf1: Box<dyn AsRef<[u8]>> = if !self.buf.is_empty() && !buf.is_empty() {
-            let mut prefix = std::mem::replace(&mut self.buf, vec![]);
-            prefix.extend_from_slice(&buf.split_to(buf.len()));
-            Box::new(prefix)
-        } else {
-            Box::new(buf.split_to(buf.len()))
-        };
-        let buf1 = buf1.as_ref().as_ref();
-        match from_utf8(buf1) {
-            Ok(s) => {
-                debug!("<< {:?}", s);
-                if !s.is_empty() {
-                    let mut buffer_queue = BufferQueue::new();
-                    let tendril = FromIterator::from_iter(s.chars());
-                    buffer_queue.push_back(tendril);
-                    self.parser.feed(&mut buffer_queue);
+        loop {
+            let token = match self.driver.parse(buf, false) {
+                Ok(Some(token)) => token,
+                Ok(None) => break,
+                Err(rxml::Error::IO(e)) if e.kind() == std::io::ErrorKind::WouldBlock => break,
+                Err(e) => return Err(minidom::Error::from(e).into()),
+            };
+
+            let had_stream_root = self.stanza_builder.depth() > 0;
+            self.stanza_builder.process_event(token)?;
+            let has_stream_root = self.stanza_builder.depth() > 0;
+
+            if !had_stream_root && has_stream_root {
+                let root = self.stanza_builder.top().unwrap();
+                let attrs =
+                    root.attrs()
+                        .map(|(name, value)| (name.to_owned(), value.to_owned()))
+                        .chain(root.prefixes.declared_prefixes().iter().map(
+                            |(prefix, namespace)| {
+                                (
+                                    prefix
+                                        .as_ref()
+                                        .map(|prefix| format!("xmlns:{}", prefix))
+                                        .unwrap_or_else(|| "xmlns".to_owned()),
+                                    namespace.clone(),
+                                )
+                            },
+                        ))
+                        .collect();
+                return Ok(Some(Packet::StreamStart(attrs)));
+            } else if self.stanza_builder.depth() == 1 {
+                self.driver.release_temporaries();
+
+                if let Some(stanza) = self.stanza_builder.unshift_child() {
+                    return Ok(Some(Packet::Stanza(stanza)));
                 }
-            }
-            // Remedies for truncated utf8
-            Err(e) if e.valid_up_to() >= buf1.len() - 3 => {
-                // Prepare all the valid data
-                let mut b = BytesMut::with_capacity(e.valid_up_to());
-                b.put(&buf1[0..e.valid_up_to()]);
+            } else if let Some(_) = self.stanza_builder.root.take() {
+                self.driver.release_temporaries();
 
-                // Retry
-                let result = self.decode(&mut b);
-
-                // Keep the tail back in
-                self.buf.extend_from_slice(&buf1[e.valid_up_to()..]);
-
-                return result;
-            }
-            Err(e) => {
-                error!(
-                    "error {} at {}/{} in {:?}",
-                    e,
-                    e.valid_up_to(),
-                    buf1.len(),
-                    buf1
-                );
-                return Err(ParserError::Utf8(e));
+                return Ok(Some(Packet::StreamEnd));
             }
         }
 
-        match self.queue.lock().unwrap().pop_front() {
-            None => Ok(None),
-            Some(result) => result.map(|pkt| Some(pkt)),
-        }
+        Ok(None)
     }
 
     fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
@@ -392,7 +230,6 @@ mod tests {
             Ok(Some(Packet::StreamStart(_))) => true,
             _ => false,
         });
-        b.clear();
         b.put_slice(b"</stream:stream>");
         let r = c.decode(&mut b);
         assert!(match r {
@@ -412,7 +249,6 @@ mod tests {
             _ => false,
         });
 
-        b.clear();
         b.put_slice("<test>ß</test".as_bytes());
         let r = c.decode(&mut b);
         assert!(match r {
@@ -420,7 +256,6 @@ mod tests {
             _ => false,
         });
 
-        b.clear();
         b.put_slice(b">");
         let r = c.decode(&mut b);
         assert!(match r {
@@ -440,7 +275,6 @@ mod tests {
             _ => false,
         });
 
-        b.clear();
         b.put(&b"<test>\xc3"[..]);
         let r = c.decode(&mut b);
         assert!(match r {
@@ -448,7 +282,6 @@ mod tests {
             _ => false,
         });
 
-        b.clear();
         b.put(&b"\x9f</test>"[..]);
         let r = c.decode(&mut b);
         assert!(match r {
@@ -469,7 +302,6 @@ mod tests {
             _ => false,
         });
 
-        b.clear();
         b.put_slice(b"<status xml:lang='en'>Test status</status>");
         let r = c.decode(&mut b);
         assert!(match r {
@@ -503,8 +335,11 @@ mod tests {
         block_on(framed.send(Packet::Stanza(stanza))).expect("send");
         assert_eq!(
             framed.get_ref().get_ref(),
-            &("<message xmlns=\"jabber:client\"><body>".to_owned() + &text + "</body></message>")
-                .as_bytes()
+            &format!(
+                "<message xmlns=\"jabber:client\"><body>{}</body></message>",
+                text
+            )
+            .as_bytes()
         );
     }
 
@@ -519,7 +354,6 @@ mod tests {
             _ => false,
         });
 
-        b.clear();
         b.put_slice(b"<message ");
         b.put_slice(b"type='chat'><body>Foo</body></message>");
         let r = c.decode(&mut b);

tokio-xmpp/src/xmpp_stream.rs πŸ”—

@@ -54,7 +54,7 @@ impl<S: AsyncRead + AsyncWrite + Unpin> XMPPStream<S> {
     }
 
     /// Send a `<stream:stream>` start tag
-    pub async fn start<'a>(stream: S, jid: Jid, ns: String) -> Result<Self, Error> {
+    pub async fn start(stream: S, jid: Jid, ns: String) -> Result<Self, Error> {
         let xmpp_stream = Framed::new(stream, XMPPCodec::new());
         stream_start::start(xmpp_stream, jid, ns).await
     }
@@ -65,7 +65,7 @@ impl<S: AsyncRead + AsyncWrite + Unpin> XMPPStream<S> {
     }
 
     /// Re-run `start()`
-    pub async fn restart<'a>(self) -> Result<Self, Error> {
+    pub async fn restart(self) -> Result<Self, Error> {
         let stream = self.stream.into_inner().unwrap().into_inner();
         Self::start(stream, self.jid, self.ns).await
     }