use crate::dto::{self, List, Timestamp, TimestampFormat};
use std::fmt;
use quick_xml::Reader;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use stdx::str::StrExt;
pub trait Deserialize<'xml>: Sized {
fn deserialize(d: &mut Deserializer<'xml>) -> DeResult<Self>;
}
pub trait DeserializeContent<'xml>: Sized {
fn deserialize_content(d: &mut Deserializer<'xml>) -> DeResult<Self>;
}
pub struct Deserializer<'xml> {
inner: Reader<&'xml [u8]>,
peeked: Option<DeEvent<'xml>>,
next_slot: Option<DeEvent<'xml>>,
}
pub type DeResult<T = (), E = DeError> = std::result::Result<T, E>;
#[derive(Debug, thiserror::Error)]
pub enum DeError {
#[error("invalid XML: {0}")]
InvalidXml(quick_xml::Error),
#[error("unexpected eof")]
UnexpectedEof,
#[error("unexpected start")]
UnexpectedStart,
#[error("unexpected end")]
UnexpectedEnd,
#[error("unexpected tag name")]
UnexpectedTagName,
#[error("invalid attribute")]
InvalidAttribute,
#[error("unexpected attribute name")]
UnexpectedAttributeName,
#[error("invalid content")]
InvalidContent,
#[error("missing field")]
MissingField,
#[error("duplicate field")]
DuplicateField,
}
#[derive(Clone)]
enum DeEvent<'xml> {
Start(BytesStart<'xml>),
End(BytesEnd<'xml>),
Text(BytesText<'xml>),
Eof,
}
impl<'xml> Deserializer<'xml> {
#[must_use]
pub fn new(xml: &'xml [u8]) -> Self {
Self {
inner: Reader::from_reader(xml),
peeked: None,
next_slot: None,
}
}
fn read_event(&mut self) -> DeResult<DeEvent<'xml>> {
if let Some(ev) = self.next_slot.take() {
return Ok(ev);
}
loop {
let ev = self.inner.read_event().map_err(invalid_xml)?;
let de = match ev {
Event::Start(x) => DeEvent::Start(x),
Event::End(x) => DeEvent::End(x),
Event::Text(x) => DeEvent::Text(x),
Event::Eof => DeEvent::Eof,
Event::Empty(x) => {
self.next_slot = Some(DeEvent::End(x.to_end().into_owned()));
DeEvent::Start(x)
}
Event::Comment(_) | Event::CData(_) | Event::Decl(_) | Event::PI(_) | Event::DocType(_) => continue,
};
break Ok(de);
}
}
fn next_event(&mut self) -> DeResult<DeEvent<'xml>> {
if let Some(ev) = self.peeked.take() {
return Ok(ev);
}
self.read_event()
}
#[allow(clippy::unwrap_used, clippy::unwrap_in_result)]
fn peek_event(&mut self) -> DeResult<DeEvent<'xml>> {
if self.peeked.is_none() {
self.peeked = Some(self.read_event()?);
}
Ok(self.peeked.clone().unwrap())
}
fn consume_peeked(&mut self) {
self.peeked = None;
}
fn expect_start(&mut self, name: &[u8]) -> DeResult {
loop {
match self.next_event()? {
DeEvent::Start(x) => {
if x.name().as_ref() != name {
return Err(unexpected_tag_name());
}
return Ok(());
}
DeEvent::End(_) => return Err(unexpected_end()),
DeEvent::Text(_) => continue,
DeEvent::Eof => return Err(unexpected_eof()),
}
}
}
fn expect_end(&mut self, name: &[u8]) -> DeResult {
loop {
match self.next_event()? {
DeEvent::Start(_) => return Err(unexpected_start()),
DeEvent::End(x) => {
if x.name().as_ref() != name {
return Err(unexpected_tag_name());
}
return Ok(());
}
DeEvent::Text(_) => continue,
DeEvent::Eof => return Err(unexpected_eof()),
}
}
}
pub fn expect_eof(&mut self) -> DeResult {
loop {
match self.next_event()? {
DeEvent::Start(_) => return Err(unexpected_start()),
DeEvent::End(_) => return Err(unexpected_end()),
DeEvent::Text(_) => continue,
DeEvent::Eof => return Ok(()),
}
}
}
pub fn named_element<T>(&mut self, name: &str, f: impl FnOnce(&mut Self) -> DeResult<T>) -> DeResult<T> {
self.expect_start(name.as_bytes())?;
let ans = f(self)?;
self.expect_end(name.as_bytes())?;
Ok(ans)
}
pub fn element<T>(&mut self, f: impl FnOnce(&mut Self, &[u8]) -> DeResult<T>) -> DeResult<T> {
loop {
match self.peek_event()? {
DeEvent::Start(start) => {
self.consume_peeked();
let name = start.name();
let ans = f(self, name.as_ref())?;
self.expect_end(name.as_ref())?;
return Ok(ans);
}
DeEvent::Text(_) => {
self.consume_peeked();
}
DeEvent::End(_) | DeEvent::Eof => {
return Err(unexpected_end());
}
}
}
}
pub fn for_each_element(&mut self, mut f: impl FnMut(&mut Self, &[u8]) -> DeResult) -> DeResult {
loop {
match self.peek_event()? {
DeEvent::Start(start) => {
self.consume_peeked();
let name = start.name();
let name = name.as_ref();
f(self, name)?;
self.expect_end(name)?;
continue;
}
DeEvent::Text(_) => {
self.consume_peeked();
continue;
}
DeEvent::End(_) | DeEvent::Eof => {
return Ok(());
}
}
}
}
pub fn for_each_element_with_start(&mut self, mut f: impl FnMut(&mut Self, &[u8], &BytesStart<'_>) -> DeResult) -> DeResult {
loop {
match self.peek_event()? {
DeEvent::Start(start) => {
self.consume_peeked();
let name = start.name();
let name = name.as_ref();
f(self, name, &start)?;
self.expect_end(name)?;
continue;
}
DeEvent::Text(_) => {
self.consume_peeked();
continue;
}
DeEvent::End(_) | DeEvent::Eof => {
return Ok(());
}
}
}
}
pub fn text<T>(&mut self, f: impl FnOnce(BytesText<'xml>) -> DeResult<T>) -> DeResult<T> {
match self.peek_event()? {
DeEvent::Start(_) => {
self.consume_peeked();
Err(unexpected_start())
}
DeEvent::End(_) => {
f(BytesText::from_escaped("")) }
DeEvent::Text(x) => {
self.consume_peeked();
f(x)
}
DeEvent::Eof => {
self.consume_peeked();
Err(unexpected_eof())
}
}
}
pub fn content<T: DeserializeContent<'xml>>(&mut self) -> DeResult<T> {
T::deserialize_content(self)
}
pub fn list_content<T: DeserializeContent<'xml>>(&mut self, name: &str) -> DeResult<List<T>> {
let mut list = List::new();
self.for_each_element(|d, x| {
if x != name.as_bytes() {
return Err(unexpected_tag_name());
}
list.push(d.content()?);
Ok(())
})?;
Ok(list)
}
pub fn timestamp(&mut self, fmt: TimestampFormat) -> DeResult<Timestamp> {
self.text(|t| {
let string = str::from_ascii_simd(t.as_ref()).map_err(|_| DeError::InvalidContent)?;
Timestamp::parse(fmt, string).map_err(|_| DeError::InvalidContent)
})
}
}
impl fmt::Debug for Deserializer<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Deserializer").finish_non_exhaustive()
}
}
const fn invalid_xml(err: quick_xml::Error) -> DeError {
DeError::InvalidXml(err)
}
const fn unexpected_eof() -> DeError {
DeError::UnexpectedEof
}
const fn unexpected_end() -> DeError {
DeError::UnexpectedEnd
}
const fn unexpected_tag_name() -> DeError {
DeError::UnexpectedTagName
}
const fn unexpected_start() -> DeError {
DeError::UnexpectedStart
}
impl<'xml> DeserializeContent<'xml> for bool {
fn deserialize_content(d: &mut Deserializer<'xml>) -> DeResult<Self> {
d.text(|t| match t.as_ref() {
b"true" | b"TRUE" => Ok(true),
b"false" | b"FALSE" => Ok(false),
_ => Err(DeError::InvalidContent),
})
}
}
impl<'xml> DeserializeContent<'xml> for String {
fn deserialize_content(d: &mut Deserializer<'xml>) -> DeResult<Self> {
d.text(|t| {
let string = t.unescape().map_err(invalid_xml)?;
Ok(string.into())
})
}
}
impl<'xml> DeserializeContent<'xml> for i32 {
fn deserialize_content(d: &mut Deserializer<'xml>) -> DeResult<Self> {
d.text(|t| atoi::atoi::<Self>(t.as_ref()).ok_or(DeError::InvalidContent))
}
}
impl<'xml> DeserializeContent<'xml> for i64 {
fn deserialize_content(d: &mut Deserializer<'xml>) -> DeResult<Self> {
d.text(|t| atoi::atoi::<Self>(t.as_ref()).ok_or(DeError::InvalidContent))
}
}
impl<'xml> DeserializeContent<'xml> for dto::Event {
fn deserialize_content(d: &mut Deserializer<'xml>) -> DeResult<Self> {
String::deserialize_content(d).map(Self::from)
}
}