use std::{fmt, io, str};
use std::borrow::Cow;
use bytes::Bytes;
use derive_more::{Display, From};
use quick_xml::events::{BytesStart, Event};
pub struct Reader<R: io::BufRead> {
reader: quick_xml::Reader<R>,
buf: Vec<u8>,
ns_buf: Vec<u8>,
}
impl<R: io::BufRead> Reader<R> {
pub fn new(reader: R) -> Self {
let mut reader = quick_xml::Reader::from_reader(reader);
reader.trim_text(true);
Reader {
reader,
buf: Vec::new(),
ns_buf: Vec::new(),
}
}
pub fn start<F, E>(&mut self, op: F) -> Result<Content, E>
where F: FnOnce(Element) -> Result<(), E>, E: From<Error> {
loop {
self.buf.clear();
let (ns, event) = self.reader.read_namespaced_event(
&mut self.buf, &mut self.ns_buf
).map_err(Into::into)?;
match event {
Event::Start(start) => {
op(Element::new(start, ns))?;
return Ok(
Content { empty: false }
)
}
Event::Empty(start) => {
op(Element::new(start, ns))?;
return Ok(
Content { empty: true }
)
}
Event::Comment(_) | Event::Decl(_) | Event::DocType(_) => { }
_ => return Err(Error::Malformed.into())
}
}
}
pub fn end(&mut self) -> Result<(), Error> {
loop {
self.buf.clear();
match self.reader.read_event(&mut self.buf)? {
Event::Eof => return Ok(()),
Event::Comment(_) => { }
_ => return Err(Error::Malformed)
}
}
}
}
pub struct Element<'b, 'n> {
start: BytesStart<'b>,
ns: Option<&'n [u8]>,
}
impl<'b, 'n> Element<'b, 'n> {
fn new(start: BytesStart<'b>, ns: Option<&'n [u8]>) -> Self {
Element { start, ns, }
}
pub fn name(&self) -> Name {
Name::new(self.ns, self.start.local_name())
}
pub fn attributes<F, E>(&self, mut op: F) -> Result<(), E>
where F: FnMut(&[u8], AttrValue) -> Result<(), E>, E: From<Error> {
for attr in self.start.attributes() {
let attr = attr.map_err(Into::into)?;
if attr.key == b"xmlns" {
continue
}
op(attr.key, AttrValue(attr))?;
}
Ok(())
}
}
pub struct Content {
empty: bool
}
impl Content {
pub fn take_element<R, F, E>(
&self,
reader: &mut Reader<R>,
op: F
) -> Result<Content, E>
where R: io::BufRead, F: FnOnce(Element) -> Result<(), E>, E: From<Error> {
if self.empty {
return Err(Error::Malformed.into())
}
loop {
reader.buf.clear();
let (ns, event) = reader.reader.read_namespaced_event(
&mut reader.buf, &mut reader.ns_buf
).map_err(Into::into)?;
match event {
Event::Start(start) => {
op(Element::new(start, ns))?;
return Ok(
Content { empty: false }
)
}
Event::Empty(start) => {
op(Element::new(start, ns))?;
return Ok(
Content { empty: false }
)
}
Event::Comment(_) => { }
_ => return Err(Error::Malformed.into())
}
}
}
pub fn take_opt_element<R, F, E>(
&mut self,
reader: &mut Reader<R>,
op: F
) -> Result<Option<Content>, E>
where R: io::BufRead, F: FnOnce(Element) -> Result<(), E>, E: From<Error> {
if self.empty {
return Ok(None)
}
loop {
reader.buf.clear();
let (ns, event) = reader.reader.read_namespaced_event(
&mut reader.buf, &mut reader.ns_buf
).map_err(Into::into)?;
match event {
Event::Start(start) => {
op(Element::new(start, ns))?;
return Ok(Some(
Content { empty: false }
))
}
Event::Empty(start) => {
op(Element::new(start, ns))?;
return Ok(Some(
Content { empty: true }
))
}
Event::End(_) => {
self.empty = true;
return Ok(None)
}
Event::Comment(_) => { }
_ => return Err(Error::Malformed.into())
}
}
}
pub fn take_text<R, F, T, E>(
&mut self,
reader: &mut Reader<R>,
op: F
) -> Result<T, E>
where R: io::BufRead, F: FnOnce(Text) -> Result<T, E>, E: From<Error> {
if self.empty {
return Err(Error::Malformed.into())
}
loop {
reader.buf.clear();
let event = reader.reader.read_event(
&mut reader.buf
).map_err(Into::into)?;
match event {
Event::Text(text) => {
return op(Text(text))
}
Event::Comment(_) => { }
_ => return Err(Error::Malformed.into())
}
}
}
pub fn take_end<R: io::BufRead>(
&mut self,
reader: &mut Reader<R>
) -> Result<(), Error> {
if self.empty {
return Ok(())
}
loop {
reader.buf.clear();
match reader.reader.read_event(&mut reader.buf)? {
Event::End(_) => {
self.empty = true;
return Ok(())
}
Event::Comment(_) => { }
_ => return Err(Error::Malformed)
}
}
}
}
#[derive(Clone, Copy,Eq, Hash, PartialEq)]
pub struct Name<'n, 'l> {
namespace: Option<&'n [u8]>,
local: &'l [u8],
}
impl<'n, 'l> Name<'n, 'l> {
fn new(namespace: Option<&'n [u8]>, local: &'l [u8]) -> Self {
Name { namespace, local }
}
pub const fn qualified(namespace: &'n [u8], local: &'l [u8]) -> Self {
Name {
namespace: Some(namespace),
local
}
}
pub const fn unqualified(local: &'l [u8]) -> Self {
Name {
namespace: None,
local
}
}
}
impl<'n, 'l> fmt::Debug for Name<'n, 'l> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Name(")?;
if let Some(ns) = self.namespace {
write!(f, "{}:", String::from_utf8_lossy(ns))?;
}
write!(f, "{}", String::from_utf8_lossy(self.local))
}
}
#[derive(Clone)]
pub struct AttrValue<'a>(quick_xml::events::attributes::Attribute<'a>);
impl<'a> AttrValue<'a> {
pub fn ascii_into<T: str::FromStr>(self) -> Result<T, Error> {
let s = self.0.unescaped_value()?;
if !s.is_ascii() {
return Err(Error::Malformed)
}
let s = unsafe { str::from_utf8_unchecked(s.as_ref()) };
T::from_str(s).map_err(|_| Error::Malformed)
}
pub fn into_ascii_bytes(self) -> Result<Bytes, Error> {
let s = self.0.unescaped_value()?;
if !s.is_ascii() {
return Err(Error::Malformed)
}
Ok(Bytes::from(unsafe { str::from_utf8_unchecked(s.as_ref()) }))
}
}
pub struct Text<'a>(quick_xml::events::BytesText<'a>);
impl<'a> Text<'a> {
pub fn to_ascii(&self) -> Result<Cow<str>, Error> {
match self.0.unescaped()? {
Cow::Borrowed(s) => {
Ok(Cow::Borrowed(
unsafe { str::from_utf8_unchecked(s) }
))
}
Cow::Owned(s) => {
Ok(Cow::Owned(unsafe { String::from_utf8_unchecked(s) }))
}
}
}
}
#[derive(Debug, Display, From)]
pub enum Error {
#[display(fmt="{}", _0)]
Xml(quick_xml::Error),
#[display(fmt="Malformed XML")]
Malformed,
}