// Copyright (c) 2020 Maxime “pep” Buquet // Copyright (c) 2020 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //! Provides a `Parser` type, which takes bytes and returns Elements. It also keeps a hold of //! ascendant elements to be able to handle namespaces properly. use crate::element::Element; use crate::error::{Error, ParserError, Result}; use bytes::BytesMut; use quick_xml::Reader as EventReader; use std::cell::RefCell; use std::str; /// Parser #[derive(Debug)] pub struct Parser { buffer: RefCell, state: ParserState, } /// Describes the state of the parser. /// /// This parser will only accept one-level documents. The root element is kept for convenience, to /// be able to pass namespaces down to children who are themselves children. #[derive(Debug)] pub enum ParserState { /// Not enough data has been processed to find the first element. Empty, /// The normal state. the root element has been identified and children are processed. Root { /// Root element. Kept for future reference root: Element, /// Child element child: Option, /// XXX: Weird flag to say if we've already sent what we could send or if there's more to /// send. This Variant needs to be changed. sent: bool, }, /// Something was passed in the buffer that made the parser get into an error state. Error, /// The root element has been closed. No feed-ing can happen past this point. Closed, } /// Result of polling the parser #[derive(Debug)] pub enum ParserResult { /// Buffer is not empty but needs more data Partial, /// An Element has been generated from the buffer. Single(Element), } /* /// Split and parse it. fn split_stream_stream_stream_features(string: String) -> (Element, Element) { let mut stuff = string.splitn(2, '>'); let stream_opening_str = stuff.next().unwrap().to_string() + "/>"; let rest = stuff.next().unwrap().to_string(); let stream_opening: Element = stream_opening_str.parse().unwrap(); let rest: Element = rest.parse().unwrap(); println!("opening: {}", String::from(&stream_opening)); println!("features: {}", String::from(&rest)); (stream_opening, rest) } */ fn maybe_split_prolog(string: &str) -> &str { if string.starts_with("'); stuff.next(); stuff.next().unwrap() } else { string } } impl Parser { /// Creates a new Parser pub fn new() -> Parser { Parser { buffer: RefCell::new(BytesMut::new()), state: ParserState::Empty, } } /// Feed bytes to the parser. pub fn feed(&mut self, bytes: BytesMut) -> Result<()> { self.buffer.borrow_mut().unsplit(bytes); let state = match self.state { ParserState::Empty => { // TODO: Try splitting xml prolog and stream header let foo = self.buffer.borrow(); let header = maybe_split_prolog(str::from_utf8(foo.as_ref())?); println!("FOO: header: {:?}", header); let mut reader = EventReader::from_str(header); let root = Element::from_reader(&mut reader); match root { Ok(root) => { println!("FOO: elem: {:?}", root); ParserState::Root { root, child: None, sent: false, } } Err(e) => { println!("FOO: err: {:?}", e); ParserState::Empty } } } ParserState::Closed => return Err(Error::ParserError(ParserError::Closed)), _ => ParserState::Empty, }; self.state = state; Ok(()) } /// Returns Elements to the application. pub fn poll(&mut self) -> Result> { match &self.state { ParserState::Empty if self.buffer.borrow().len() != 0 => { Ok(Some(ParserResult::Partial)) } ParserState::Empty | ParserState::Closed | ParserState::Error => Ok(None), ParserState::Root { root, child: None, .. } => Ok(Some(ParserResult::Single(root.clone()))), ParserState::Root { child: Some(child), .. } => Ok(Some(ParserResult::Single(child.clone()))), } } /// Resets the parser pub fn reset(&mut self) { *self = Parser::new(); } } #[cfg(test)] mod tests { use super::*; use bytes::{BufMut, BytesMut}; #[test] fn test_prolog() { let mut parser = Parser::new(); let mut buf = BytesMut::new(); buf.put(&b""[..]); buf.put(&b""[..]); match parser.feed(buf) { Ok(_) => (), _ => panic!(), } let elem = Element::builder("stream:stream", "http://etherx.jabber.org/streams") .prefix_ns(None, "jabber:client") .attr("xml:lang", "en") .attr("version", "1.0") .attr("to", "foo.bar") .build(); println!("BAR: elem: {:?}", elem); match parser.poll() { Ok(Some(ParserResult::Single(e))) => assert_eq!(e, elem), _ => panic!(), } } }