use base64::{self, Engine};
use base64::DecodeError;
use bytes::Bytes;
use hex;
use hex::FromHexError;
use std::fs::File;
use std::path::Path;
use std::{fs, io};
use xmlrs::attribute::OwnedAttribute;
use xmlrs::reader::XmlEvent;
use xmlrs::{reader, writer};
use xmlrs::{EmitterConfig, EventReader, EventWriter, ParserConfig};
pub struct XmlReader<R: io::Read> {
reader: EventReader<R>,
cached_event: Option<XmlEvent>,
next_start_name: Option<String>,
}
impl<R: io::Read> XmlReader<R> {
fn next(&mut self) -> Result<XmlEvent, XmlReaderErr> {
match self.cached_event.take() {
Some(e) => Ok(e),
None => Ok(self.reader.next()?),
}
}
fn cache(&mut self, e: XmlEvent) {
self.cached_event = Some(e);
}
}
impl<R: io::Read> XmlReader<R> {
fn start_document(&mut self) -> Result<(), XmlReaderErr> {
match self.next() {
Ok(reader::XmlEvent::StartDocument { .. }) => Ok(()),
_ => Err(XmlReaderErr::ExpectedStartDocument),
}
}
fn expect_element(&mut self) -> Result<(Tag, Attributes), XmlReaderErr> {
match self.next() {
Ok(reader::XmlEvent::StartElement {
name, attributes, ..
}) => Ok((
Tag {
name: name.local_name,
},
Attributes { attributes },
)),
_ => Err(XmlReaderErr::ExpectedStart),
}
}
fn expect_close(&mut self, tag: Tag) -> Result<(), XmlReaderErr> {
match self.next() {
Ok(reader::XmlEvent::EndElement { name, .. }) => {
if name.local_name == tag.name {
Ok(())
} else {
Err(XmlReaderErr::ExpectedClose(tag.name))
}
}
_ => Err(XmlReaderErr::ExpectedClose(tag.name)),
}
}
fn end_document(&mut self) -> Result<(), XmlReaderErr> {
match self.next() {
Ok(reader::XmlEvent::EndDocument) => Ok(()),
_ => Err(XmlReaderErr::ExpectedEnd),
}
}
}
impl<R: io::Read> XmlReader<R> {
pub fn decode<F, T, E>(source: R, op: F) -> Result<T, E>
where
F: FnOnce(&mut Self) -> Result<T, E>,
E: From<XmlReaderErr>,
{
let mut config = ParserConfig::new();
config.trim_whitespace = true;
config.ignore_comments = true;
let mut xml = XmlReader {
reader: config.create_reader(source),
cached_event: None,
next_start_name: None,
};
xml.start_document()?;
let res = op(&mut xml)?;
xml.end_document()?;
Ok(res)
}
pub fn take_element<F, T, E>(&mut self, op: F) -> Result<T, E>
where
F: FnOnce(&Tag, Attributes, &mut Self) -> Result<T, E>,
E: From<XmlReaderErr>,
{
let (tag, attr) = self.expect_element()?;
let res = op(&tag, attr, self)?;
self.expect_close(tag)?;
Ok(res)
}
pub fn take_named_element<F, T, E>(&mut self, name: &str, op: F) -> Result<T, E>
where
F: FnOnce(Attributes, &mut Self) -> Result<T, E>,
E: From<XmlReaderErr>,
{
self.take_element(|t, a, r| {
if t.name != name {
Err(XmlReaderErr::ExpectedNamedStart(name.to_string()).into())
} else {
op(a, r)
}
})
}
pub fn take_opt_element<F, T, E>(&mut self, op: F) -> Result<Option<T>, E>
where
F: FnOnce(&Tag, Attributes, &mut Self) -> Result<Option<T>, E>,
E: From<XmlReaderErr>,
{
let n = self.next()?;
match n {
XmlEvent::StartElement {
name, attributes, ..
} => {
let tag = Tag {
name: name.local_name,
};
let res = op(&tag, Attributes { attributes }, self)?;
self.expect_close(tag)?;
Ok(res)
}
_ => {
self.cache(n);
Ok(None)
}
}
}
pub fn take_chars(&mut self) -> Result<String, XmlReaderErr> {
match self.next() {
Ok(reader::XmlEvent::Characters(chars)) => Ok(chars),
_ => Err(XmlReaderErr::ExpectedCharacters),
}
}
pub fn take_bytes_std(&mut self) -> Result<Bytes, XmlReaderErr> {
self.take_bytes(base64::engine::general_purpose::STANDARD_NO_PAD)
}
pub fn take_bytes_url_safe_pad(&mut self) -> Result<Bytes, XmlReaderErr> {
self.take_bytes(base64::engine::general_purpose::URL_SAFE_NO_PAD)
}
fn take_bytes(&mut self, config: base64::engine::GeneralPurpose) -> Result<Bytes, XmlReaderErr> {
let chars = self.take_chars()?;
let chars: Vec<u8> = chars
.into_bytes()
.into_iter()
.filter(|c| !b" \n\t\r\x0b\x0c=".contains(c))
.collect();
let b64 = config.decode(&chars)?;
Ok(Bytes::from(b64))
}
pub fn take_empty(&mut self) -> Result<(), XmlReaderErr> {
Ok(())
}
pub fn next_start_name(&mut self) -> Option<&str> {
match self.next() {
Err(_) => None,
Ok(e) => {
if let XmlEvent::StartElement { ref name, .. } = e {
self.next_start_name = Some(name.local_name.clone())
} else {
self.next_start_name = None;
}
self.cache(e);
self.next_start_name.as_ref().map(AsRef::as_ref)
}
}
}
}
impl XmlReader<fs::File> {
pub fn open<P, F, T, E>(path: P, op: F) -> Result<T, E>
where
F: FnOnce(&mut Self) -> Result<T, E>,
P: AsRef<Path>,
E: From<XmlReaderErr> + From<io::Error>,
{
Self::decode(fs::File::open(path)?, op)
}
}
#[derive(Debug, Display)]
pub enum XmlReaderErr {
#[display("Expected Start of Document")]
ExpectedStartDocument,
#[display("Expected Start Element")]
ExpectedStart,
#[display("Expected Start Element with name: {}", _0)]
ExpectedNamedStart(String),
#[display("Expected Characters Element")]
ExpectedCharacters,
#[display("Expected Close Element with name: {}", _0)]
ExpectedClose(String),
#[display("Expected End of Document")]
ExpectedEnd,
#[display("Error reading file: {}", _0)]
IoError(io::Error),
#[display("Attributes Error: {}", _0)]
AttributesError(AttributesError),
#[display("XML Reader Error: {}", _0)]
ReaderError(reader::Error),
#[display("Base64 decoding issue: {}", _0)]
Base64Error(DecodeError),
}
impl From<io::Error> for XmlReaderErr {
fn from(e: io::Error) -> XmlReaderErr {
XmlReaderErr::IoError(e)
}
}
impl From<AttributesError> for XmlReaderErr {
fn from(e: AttributesError) -> XmlReaderErr {
XmlReaderErr::AttributesError(e)
}
}
impl From<reader::Error> for XmlReaderErr {
fn from(e: reader::Error) -> XmlReaderErr {
XmlReaderErr::ReaderError(e)
}
}
impl From<DecodeError> for XmlReaderErr {
fn from(e: DecodeError) -> XmlReaderErr {
XmlReaderErr::Base64Error(e)
}
}
pub struct Attributes {
attributes: Vec<OwnedAttribute>,
}
impl Attributes {
pub fn take_opt(&mut self, name: &str) -> Option<String> {
let i = self
.attributes
.iter()
.position(|a| a.name.local_name == name);
match i {
Some(i) => {
let a = self.attributes.swap_remove(i);
Some(a.value)
}
None => None,
}
}
pub fn take_opt_hex(&mut self, name: &str) -> Option<Bytes> {
self.take_req_hex(name).ok()
}
pub fn take_req(&mut self, name: &str) -> Result<String, AttributesError> {
self.take_opt(name)
.ok_or_else(|| AttributesError::MissingAttribute(name.to_string()))
}
pub fn take_req_hex(&mut self, name: &str) -> Result<Bytes, AttributesError> {
match hex::decode(self.take_req(name)?) {
Err(e) => Err(AttributesError::HexError(e)),
Ok(b) => Ok(Bytes::from(b)),
}
}
pub fn exhausted(&self) -> Result<(), AttributesError> {
if self.attributes.is_empty() {
Ok(())
} else {
Err(AttributesError::extras(&self.attributes))
}
}
}
#[derive(Debug, Display)]
pub enum AttributesError {
#[display("Required attribute missing: {}", _0)]
MissingAttribute(String),
#[display("Extra attributes found: {}", _0)]
ExtraAttributes(String),
#[display("Wrong hex encoding: {}", _0)]
HexError(FromHexError),
}
impl AttributesError {
fn extras(atts: &[OwnedAttribute]) -> Self {
let atts: Vec<String> = atts.iter().map(|a| format!("{a}")).collect();
let atts = atts.join(", ");
AttributesError::ExtraAttributes(atts)
}
}
pub struct Tag {
pub name: String,
}
pub struct XmlWriter<W> {
writer: EventWriter<W>,
}
impl<W: io::Write> XmlWriter<W> {
fn unwrap_emitter_error<T>(r: Result<T, writer::Error>) -> Result<T, io::Error> {
match r {
Ok(t) => Ok(t),
Err(e) => {
match e {
writer::Error::Io(io) => Err(io),
_ => {
panic!("XmlWriter library error: {e:?}")
}
}
}
}
}
pub fn put_element<F>(
&mut self,
name: &str,
attr: Option<&[(&str, &str)]>,
op: F,
) -> Result<(), io::Error>
where
F: FnOnce(&mut Self) -> Result<(), io::Error>,
{
let mut start = writer::XmlEvent::start_element(name);
if let Some(v) = attr {
for a in v {
start = start.attr(a.0, a.1);
}
}
Self::unwrap_emitter_error(self.writer.write(start))?;
op(self)?;
Self::unwrap_emitter_error(self.writer.write(writer::XmlEvent::end_element()))?;
Ok(())
}
pub fn put_text(&mut self, text: &str) -> Result<(), io::Error> {
Self::unwrap_emitter_error(self.writer.write(writer::XmlEvent::Characters(text)))?;
Ok(())
}
pub fn put_base64_std(&mut self, bytes: &Bytes) -> Result<(), io::Error> {
let b64 = base64::engine::general_purpose::STANDARD.encode(bytes);
self.put_text(b64.as_ref())
}
pub fn put_base64_url_safe(&mut self, bytes: &[u8]) -> Result<(), io::Error> {
let b64 = base64::engine::general_purpose::URL_SAFE.encode(bytes);
self.put_text(b64.as_ref())
}
pub fn empty(&mut self) -> Result<(), io::Error> {
Ok(())
}
fn encode<F>(w: W, op: F) -> Result<(), io::Error>
where
F: FnOnce(&mut Self) -> Result<(), io::Error>,
{
let writer = EmitterConfig::new()
.write_document_declaration(false)
.normalize_empty_elements(true)
.perform_indent(true)
.create_writer(w);
let mut x = XmlWriter { writer };
op(&mut x)
}
}
impl XmlWriter<()> {
pub fn encode_vec<F>(op: F) -> Vec<u8>
where
F: FnOnce(&mut XmlWriter<&mut Vec<u8>>) -> Result<(), io::Error>,
{
let mut b = Vec::new();
XmlWriter::encode(&mut b, op).unwrap(); b
}
pub fn encode_to_file<F>(file: &mut File, op: F) -> Result<(), io::Error>
where
F: FnOnce(&mut XmlWriter<&mut File>) -> Result<(), io::Error>,
{
XmlWriter::encode(file, op)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str;
#[test]
fn should_write_xml() {
let xml = XmlWriter::encode_vec(|w| {
w.put_element("a", Some(&[("xmlns", "http://ns/"), ("c", "d")]), |w| {
w.put_element("b", None, |w| w.put_base64_std(&Bytes::from("X")))
})
});
assert_eq!(
str::from_utf8(&xml).unwrap(),
"<a xmlns=\"http://ns/\" c=\"d\">\n <b>WA==</b>\n</a>"
);
}
}