xmpp-rs/minidom/src/parser.rs

184 lines
5.7 KiB
Rust
Raw Normal View History

minidom: forcing a namespace on Element. Stop requiring prefixes. Below is what I think I did. A few changes: - Change prefixes to be something less important in the API. - Rework the Element struct to force a namespace. In XMPP everything is namespaced. - Remove parent ref on what was previously NamespaceSet and is now Prefixes. More specifically this means `Element::new` has changed to require `Element`'s new new properties as parameters. `Element::builder` and `Element::bare` now require a namespace unconditionally. `Element::prefix` has been removed. This new API is based on the fact that prefixes are non-essential (really just an implementation detail) and shouldn't be visible to the user. It is possible nonetheless to set custom prefixes for compatibility reasons with `ElementBuilder::prefix`. **A prefix is firstly mapped to a namespace, and then attached to an element**, there cannot be a prefix without a namespace. Prefix inheritance is used if possible but for the case with no prefix ("xmlns") to be reused, we only check custom prefixes declared on the tag itself and not ascendants. If it's already used then we generate prefixes (ns0, ns1, ..) checking on what has been declared on all ascendants (plus of course those already set on the current tag). Example API: ```rust let mut elem = ElementBuilder("stream", "http://etherx.jabber.org/streams") .prefix(Some(String::from("stream")), "http://etherx.jabber.org/streams) .prefix(None, "jabber:client") .attr(..) .build(); assert_eq!(elem.ns(), String::from("http://etherx.jabber.org/streams")); ``` See also the few tests added in src/tests. TODO: Fix inconsistencies wrt. "prefix:name" format provided as a name when creating an Element with `Element::new` or `Element::bare`. `Element::builder` already handles this as it should, splitting name and prefix. TODO: Change `Element::name` method to `Element::local_name` to make it more explicit. Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2020-03-27 13:45:22 +01:00
// 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!(),
}
}
}