diff --git a/instant-xml-macros/src/lib.rs b/instant-xml-macros/src/lib.rs index 02146c0..8f50b66 100644 --- a/instant-xml-macros/src/lib.rs +++ b/instant-xml-macros/src/lib.rs @@ -136,7 +136,7 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream { impl #generics ToXml for #ident #generics { fn serialize(&self, serializer: &mut instant_xml::Serializer) -> Result<(), instant_xml::Error> { let _ = serializer.consume_field_context(); - let mut field_context = instant_xml::FieldContext { + let mut field_context = instant_xml::ser::FieldContext { name: #root_name, attribute: None, }; diff --git a/instant-xml-macros/src/ser.rs b/instant-xml-macros/src/ser.rs index 30bdb5a..6542b20 100644 --- a/instant-xml-macros/src/ser.rs +++ b/instant-xml-macros/src/ser.rs @@ -81,7 +81,7 @@ impl<'a> Serializer { let field_value = field.ident.as_ref().unwrap(); let declaration = quote!( - let mut field = instant_xml::FieldContext { + let mut field = instant_xml::ser::FieldContext { name: #name, attribute: None, }; diff --git a/instant-xml/src/lib.rs b/instant-xml/src/lib.rs index cc16c71..1dd53b1 100644 --- a/instant-xml/src/lib.rs +++ b/instant-xml/src/lib.rs @@ -1,16 +1,16 @@ -use std::collections::HashMap; use std::fmt; -use std::fmt::Write; use thiserror::Error; -pub use xmlparser; pub use macros::{FromXml, ToXml}; -mod impls; #[doc(hidden)] pub mod de; +mod impls; pub use de::Deserializer; +#[doc(hidden)] +pub mod ser; +pub use ser::Serializer; pub trait ToXml { fn to_xml(&self) -> Result { @@ -23,142 +23,12 @@ pub trait ToXml { fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error>; } -pub struct Serializer<'xml, W: fmt::Write> { - // For parent namespaces the key is the namespace and the value is the prefix. We are adding to map - // only if the namespaces do not exist, if it does exist then we are using an already defined parent prefix. - #[doc(hidden)] - pub parent_namespaces: HashMap<&'xml str, &'xml str>, - #[doc(hidden)] - pub output: &'xml mut W, - - parent_default_namespace: &'xml str, - parent_default_namespace_to_revert: &'xml str, - current_attributes: String, - next_field_context: Option>, -} - -impl<'xml, W: fmt::Write> Serializer<'xml, W> { - pub fn new(output: &'xml mut W) -> Self { - Self { - parent_namespaces: HashMap::new(), - output, - parent_default_namespace: "", - parent_default_namespace_to_revert: "", - next_field_context: None, - current_attributes: String::new(), - } - } - - pub fn consume_current_attributes(&mut self) -> Result<(), Error> { - self.output.write_str(&self.current_attributes)?; - self.current_attributes.clear(); - Ok(()) - } - - pub fn add_attribute_key(&mut self, attr_key: &impl fmt::Display) -> Result<(), Error> { - self.current_attributes.push(' '); - write!(self.current_attributes, "{}", attr_key)?; - self.current_attributes.push('='); - Ok(()) - } - - pub fn add_attribute_value(&mut self, attr_value: &impl fmt::Display) -> Result<(), Error> { - self.current_attributes.push('"'); - write!(self.current_attributes, "{}", attr_value)?; - self.current_attributes.push('"'); - Ok(()) - } - - pub fn set_field_context(&mut self, field_context: FieldContext<'xml>) -> Result<(), Error> { - if self.next_field_context.is_some() { - return Err(Error::UnexpectedState); - }; - - self.next_field_context = Some(field_context); - Ok(()) - } - - pub fn consume_field_context(&mut self) -> Option> { - self.next_field_context.take() - } - - pub fn set_parent_default_namespace(&mut self, namespace: &'xml str) -> Result<(), Error> { - self.parent_default_namespace = namespace; - Ok(()) - } - - pub fn parent_default_namespace(&self) -> &'xml str { - self.parent_default_namespace - } - - pub fn update_parent_default_namespace(&mut self, namespace: &'xml str) { - self.parent_default_namespace_to_revert = self.parent_default_namespace; - self.parent_default_namespace = namespace; - } - - pub fn retrieve_parent_default_namespace(&mut self) { - self.parent_default_namespace = self.parent_default_namespace_to_revert; - } - - fn add_open_tag(&mut self, field_context: &FieldContext) -> Result<(), Error> { - match field_context.attribute { - Some(FieldAttribute::Prefix(prefix)) => { - self.output.write_char('<')?; - self.output.write_str(prefix)?; - self.output.write_char(':')?; - self.output.write_str(field_context.name)?; - self.output.write_char('>')?; - } - Some(FieldAttribute::Namespace(namespace)) - if self.parent_default_namespace != namespace => - { - self.output.write_char('<')?; - self.output.write_str(field_context.name)?; - self.output.write_str(" xmlns=\"")?; - self.output.write_str(namespace)?; - self.output.write_str("\">")?; - } - _ => { - self.output.write_char('<')?; - self.output.write_str(field_context.name)?; - self.output.write_char('>')?; - } - } - Ok(()) - } - - fn add_close_tag(&mut self, field_context: FieldContext) -> Result<(), Error> { - match field_context.attribute { - Some(FieldAttribute::Prefix(prefix)) => { - self.output.write_str("')?; - } - _ => { - self.output.write_str("')?; - } - } - Ok(()) - } -} - pub enum FieldAttribute<'xml> { Prefix(&'xml str), Namespace(&'xml str), Attribute, } -pub struct FieldContext<'xml> { - #[doc(hidden)] - pub name: &'xml str, - #[doc(hidden)] - pub attribute: Option>, -} - pub enum TagName { FieldName, Custom(&'static str), diff --git a/instant-xml/src/ser.rs b/instant-xml/src/ser.rs new file mode 100644 index 0000000..eb3ce61 --- /dev/null +++ b/instant-xml/src/ser.rs @@ -0,0 +1,134 @@ +use std::collections::HashMap; +use std::fmt::{self, Write}; + +use super::{Error, FieldAttribute}; + +pub struct Serializer<'xml, W: fmt::Write> { + // For parent namespaces the key is the namespace and the value is the prefix. We are adding to map + // only if the namespaces do not exist, if it does exist then we are using an already defined parent prefix. + #[doc(hidden)] + pub parent_namespaces: HashMap<&'xml str, &'xml str>, + #[doc(hidden)] + pub output: &'xml mut W, + + parent_default_namespace: &'xml str, + parent_default_namespace_to_revert: &'xml str, + current_attributes: String, + next_field_context: Option>, +} + +impl<'xml, W: fmt::Write> Serializer<'xml, W> { + pub fn new(output: &'xml mut W) -> Self { + Self { + parent_namespaces: HashMap::new(), + output, + parent_default_namespace: "", + parent_default_namespace_to_revert: "", + next_field_context: None, + current_attributes: String::new(), + } + } + + pub fn consume_current_attributes(&mut self) -> Result<(), Error> { + self.output.write_str(&self.current_attributes)?; + self.current_attributes.clear(); + Ok(()) + } + + pub fn add_attribute_key(&mut self, attr_key: &impl fmt::Display) -> Result<(), Error> { + self.current_attributes.push(' '); + write!(self.current_attributes, "{}", attr_key)?; + self.current_attributes.push('='); + Ok(()) + } + + pub fn add_attribute_value(&mut self, attr_value: &impl fmt::Display) -> Result<(), Error> { + self.current_attributes.push('"'); + write!(self.current_attributes, "{}", attr_value)?; + self.current_attributes.push('"'); + Ok(()) + } + + pub fn set_field_context(&mut self, field_context: FieldContext<'xml>) -> Result<(), Error> { + if self.next_field_context.is_some() { + return Err(Error::UnexpectedState); + }; + + self.next_field_context = Some(field_context); + Ok(()) + } + + pub fn consume_field_context(&mut self) -> Option> { + self.next_field_context.take() + } + + pub fn set_parent_default_namespace(&mut self, namespace: &'xml str) -> Result<(), Error> { + self.parent_default_namespace = namespace; + Ok(()) + } + + pub fn parent_default_namespace(&self) -> &'xml str { + self.parent_default_namespace + } + + pub fn update_parent_default_namespace(&mut self, namespace: &'xml str) { + self.parent_default_namespace_to_revert = self.parent_default_namespace; + self.parent_default_namespace = namespace; + } + + pub fn retrieve_parent_default_namespace(&mut self) { + self.parent_default_namespace = self.parent_default_namespace_to_revert; + } + + pub fn add_open_tag(&mut self, field_context: &FieldContext) -> Result<(), Error> { + match field_context.attribute { + Some(FieldAttribute::Prefix(prefix)) => { + self.output.write_char('<')?; + self.output.write_str(prefix)?; + self.output.write_char(':')?; + self.output.write_str(field_context.name)?; + self.output.write_char('>')?; + } + Some(FieldAttribute::Namespace(namespace)) + if self.parent_default_namespace != namespace => + { + self.output.write_char('<')?; + self.output.write_str(field_context.name)?; + self.output.write_str(" xmlns=\"")?; + self.output.write_str(namespace)?; + self.output.write_str("\">")?; + } + _ => { + self.output.write_char('<')?; + self.output.write_str(field_context.name)?; + self.output.write_char('>')?; + } + } + Ok(()) + } + + pub fn add_close_tag(&mut self, field_context: FieldContext) -> Result<(), Error> { + match field_context.attribute { + Some(FieldAttribute::Prefix(prefix)) => { + self.output.write_str("')?; + } + _ => { + self.output.write_str("')?; + } + } + Ok(()) + } +} + +pub struct FieldContext<'xml> { + #[doc(hidden)] + pub name: &'xml str, + #[doc(hidden)] + pub attribute: Option>, +}