diff --git a/instant-xml-macros/src/lib.rs b/instant-xml-macros/src/lib.rs index 73c4c9b..a47580e 100644 --- a/instant-xml-macros/src/lib.rs +++ b/instant-xml-macros/src/lib.rs @@ -20,3 +20,21 @@ pub fn to_xml(input: TokenStream) -> TokenStream { } )) } + +#[proc_macro_derive(FromXml)] +pub fn from_xml(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as syn::ItemStruct); + let ident = &ast.ident; + let name = ident.to_string(); + TokenStream::from(quote!( + impl<'xml> FromXml<'xml> for #ident { + fn from_xml(input: &str) -> Result { + use ::instant_xml::parse::Parse; + let mut iter = ::instant_xml::xmlparser::Tokenizer::from(input); + iter.next().element_start(None, #name)?; + iter.next().element_end(None, #name)?; + Ok(Self) + } + } + )) +} diff --git a/instant-xml/src/lib.rs b/instant-xml/src/lib.rs index c6c5cbc..222475d 100644 --- a/instant-xml/src/lib.rs +++ b/instant-xml/src/lib.rs @@ -1,8 +1,13 @@ use std::fmt; +use std::collections::HashMap; +pub use xmlparser as xmlparser; use thiserror::Error; -pub use macros::ToXml; +pub use macros::{FromXml, ToXml}; + +#[doc(hidden)] +pub mod parse; pub trait ToXml { fn write_xml(&self, write: &mut W) -> Result<(), Error>; @@ -20,8 +25,18 @@ pub trait FromXml<'xml>: Sized { pub trait FromXmlOwned: for<'xml> FromXml<'xml> {} +struct State<'a> { + prefix: HashMap<&'a str, &'a str>, +} + #[derive(Debug, Error)] pub enum Error { #[error("format: {0}")] Format(#[from] fmt::Error), + #[error("parse: {0}")] + Parse(#[from] xmlparser::Error), + #[error("unexpected end of stream")] + UnexpectedEndOfStream, + #[error("unexpected value")] + UnexpectedValue, } diff --git a/instant-xml/src/parse.rs b/instant-xml/src/parse.rs new file mode 100644 index 0000000..d9bf73d --- /dev/null +++ b/instant-xml/src/parse.rs @@ -0,0 +1,50 @@ +use xmlparser::{Token, ElementEnd}; + +use super::Error; + +impl<'a> Parse for Option, xmlparser::Error>> { + fn element_start(self, ns: Option<&str>, tag: &str) -> Result<(), Error> { + match self { + Some(Ok(Token::ElementStart { prefix, local, .. })) => { + let prefix_ns = prefix.as_str(); + let (has_prefix, expect_prefix) = (!prefix_ns.is_empty(), ns.is_some()); + if has_prefix != expect_prefix { + return dbg!(Err(Error::UnexpectedValue)); + } + + if has_prefix && Some(prefix_ns) != ns { + return dbg!(Err(Error::UnexpectedValue)); + } + + if local.as_str() != tag { + return dbg!(Err(Error::UnexpectedValue)); + } + + Ok(()) + } + Some(Ok(_)) => Err(Error::UnexpectedValue), + Some(Err(err)) => Err(err.into()), + None => Err(Error::UnexpectedEndOfStream), + } + } + + fn element_end(self, _: Option<&str>, _: &str) -> Result<(), Error> { + match self { + Some(Ok(Token::ElementEnd { end, .. })) => { + match end { + ElementEnd::Open => todo!(), + ElementEnd::Close(_, _) => todo!(), + ElementEnd::Empty => return Ok(()), + } + } + Some(Ok(_)) => Err(Error::UnexpectedValue), + Some(Err(err)) => Err(err.into()), + None => Err(Error::UnexpectedEndOfStream), + } + } +} + +pub trait Parse { + fn element_start(self, ns: Option<&str>, tag: &str) -> Result<(), Error>; + fn element_end(self, ns: Option<&str>, tag: &str) -> Result<(), Error>; +} diff --git a/instant-xml/tests/all.rs b/instant-xml/tests/all.rs index b347244..b159bc5 100644 --- a/instant-xml/tests/all.rs +++ b/instant-xml/tests/all.rs @@ -1,9 +1,10 @@ -use instant_xml::ToXml; +use instant_xml::{FromXml, ToXml}; -#[derive(ToXml)] +#[derive(Debug, Eq, FromXml, PartialEq, ToXml)] struct Unit; #[test] fn unit() { assert_eq!(Unit.to_xml().unwrap(), ""); + assert_eq!(Unit::from_xml("").unwrap(), Unit); }