Turn serializer API into a proper abstraction
This commit is contained in:
parent
0f22e36844
commit
6238ed87c5
|
@ -1,7 +1,7 @@
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
use crate::{ContainerMeta, FieldMeta, Namespace};
|
use super::{discard_lifetimes, ContainerMeta, FieldMeta, Namespace};
|
||||||
|
|
||||||
pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
|
pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
|
||||||
let ident = &input.ident;
|
let ident = &input.ident;
|
||||||
|
@ -80,8 +80,8 @@ pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
|
||||||
quote!(
|
quote!(
|
||||||
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
|
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
|
||||||
fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result<Self, ::instant_xml::Error> {
|
fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result<Self, ::instant_xml::Error> {
|
||||||
use ::instant_xml::de::{Deserializer, Id, Node};
|
use ::instant_xml::de::{Deserializer, Node};
|
||||||
use ::instant_xml::Error;
|
use ::instant_xml::{Error, Id};
|
||||||
use ::core::marker::PhantomData;
|
use ::core::marker::PhantomData;
|
||||||
|
|
||||||
enum __Elements {
|
enum __Elements {
|
||||||
|
@ -142,7 +142,7 @@ pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
|
||||||
Ok(Self { #return_val })
|
Ok(Self { #return_val })
|
||||||
}
|
}
|
||||||
|
|
||||||
const KIND: ::instant_xml::de::Kind = ::instant_xml::de::Kind::Element(::instant_xml::de::Id {
|
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element(::instant_xml::Id {
|
||||||
ns: #default_namespace,
|
ns: #default_namespace,
|
||||||
name: #name,
|
name: #name,
|
||||||
});
|
});
|
||||||
|
@ -181,7 +181,7 @@ fn process_field(
|
||||||
};
|
};
|
||||||
|
|
||||||
tokens.consts.extend(quote!(
|
tokens.consts.extend(quote!(
|
||||||
const #const_field_var_str: Id<'static> = <#no_lifetime_type>::KIND.name(
|
const #const_field_var_str: Id<'static> = <#no_lifetime_type as FromXml<'_>>::KIND.name(
|
||||||
Id { ns: #ns, name: #field_var_str }
|
Id { ns: #ns, name: #field_var_str }
|
||||||
);
|
);
|
||||||
));
|
));
|
||||||
|
@ -249,40 +249,3 @@ impl Default for Tokens {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discard_lifetimes(ty: &mut syn::Type) {
|
|
||||||
match ty {
|
|
||||||
syn::Type::Path(ty) => discard_path_lifetimes(ty),
|
|
||||||
syn::Type::Reference(ty) => {
|
|
||||||
ty.lifetime = None;
|
|
||||||
discard_lifetimes(&mut ty.elem);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn discard_path_lifetimes(path: &mut syn::TypePath) {
|
|
||||||
if let Some(q) = &mut path.qself {
|
|
||||||
discard_lifetimes(&mut q.ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
for segment in &mut path.path.segments {
|
|
||||||
match &mut segment.arguments {
|
|
||||||
syn::PathArguments::None => {}
|
|
||||||
syn::PathArguments::AngleBracketed(args) => {
|
|
||||||
args.args.iter_mut().for_each(|arg| match arg {
|
|
||||||
syn::GenericArgument::Lifetime(lt) => {
|
|
||||||
*lt = syn::Lifetime::new("'_", Span::call_site())
|
|
||||||
}
|
|
||||||
syn::GenericArgument::Type(ty) => discard_lifetimes(ty),
|
|
||||||
syn::GenericArgument::Binding(_)
|
|
||||||
| syn::GenericArgument::Constraint(_)
|
|
||||||
| syn::GenericArgument::Const(_) => {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
syn::PathArguments::Parenthesized(args) => {
|
|
||||||
args.inputs.iter_mut().for_each(discard_lifetimes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ mod ser;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, TokenStream, TokenTree};
|
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::parse_macro_input;
|
use syn::parse_macro_input;
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
|
@ -455,3 +455,40 @@ impl ToTokens for Namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn discard_lifetimes(ty: &mut syn::Type) {
|
||||||
|
match ty {
|
||||||
|
syn::Type::Path(ty) => discard_path_lifetimes(ty),
|
||||||
|
syn::Type::Reference(ty) => {
|
||||||
|
ty.lifetime = None;
|
||||||
|
discard_lifetimes(&mut ty.elem);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discard_path_lifetimes(path: &mut syn::TypePath) {
|
||||||
|
if let Some(q) = &mut path.qself {
|
||||||
|
discard_lifetimes(&mut q.ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
for segment in &mut path.path.segments {
|
||||||
|
match &mut segment.arguments {
|
||||||
|
syn::PathArguments::None => {}
|
||||||
|
syn::PathArguments::AngleBracketed(args) => {
|
||||||
|
args.args.iter_mut().for_each(|arg| match arg {
|
||||||
|
syn::GenericArgument::Lifetime(lt) => {
|
||||||
|
*lt = syn::Lifetime::new("'_", Span::call_site())
|
||||||
|
}
|
||||||
|
syn::GenericArgument::Type(ty) => discard_lifetimes(ty),
|
||||||
|
syn::GenericArgument::Binding(_)
|
||||||
|
| syn::GenericArgument::Constraint(_)
|
||||||
|
| syn::GenericArgument::Const(_) => {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
syn::PathArguments::Parenthesized(args) => {
|
||||||
|
args.inputs.iter_mut().for_each(discard_lifetimes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
use crate::{ContainerMeta, FieldMeta};
|
use super::{discard_lifetimes, ContainerMeta, FieldMeta};
|
||||||
|
|
||||||
pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
let mut body = TokenStream::new();
|
let mut body = TokenStream::new();
|
||||||
|
@ -26,11 +26,7 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
for (key, val) in &meta.ns.prefixes {
|
for (key, val) in &meta.ns.prefixes {
|
||||||
prefixes.extend(quote!(
|
prefixes.extend(quote!(
|
||||||
if serializer.parent_namespaces.get(#val).is_none() {
|
if serializer.parent_namespaces.get(#val).is_none() {
|
||||||
serializer.output.write_str(" xmlns:")?;
|
serializer.write_prefix(#key, #val)?;
|
||||||
serializer.output.write_str(#key)?;
|
|
||||||
serializer.output.write_str("=\"")?;
|
|
||||||
serializer.output.write_str(#val)?;
|
|
||||||
serializer.output.write_char('\"')?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ::std::collections::hash_map::Entry::Vacant(v) = serializer.parent_namespaces.entry(#val) {
|
if let ::std::collections::hash_map::Entry::Vacant(v) = serializer.parent_namespaces.entry(#val) {
|
||||||
|
@ -55,44 +51,22 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
&self,
|
&self,
|
||||||
serializer: &mut instant_xml::Serializer<W>,
|
serializer: &mut instant_xml::Serializer<W>,
|
||||||
) -> Result<(), instant_xml::Error> {
|
) -> Result<(), instant_xml::Error> {
|
||||||
use ::instant_xml::ser::{FieldAttribute, FieldContext};
|
|
||||||
|
|
||||||
let _ = serializer.consume_field_context();
|
|
||||||
let mut field_context = FieldContext {
|
|
||||||
name: #root_name,
|
|
||||||
attribute: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start tag
|
// Start tag
|
||||||
serializer.output.write_char('<')?;
|
match serializer.parent_default_namespace() == #default_namespace {
|
||||||
if serializer.parent_default_namespace() != #default_namespace {
|
true => serializer.write_start(None, #root_name, None)?,
|
||||||
if let Some(prefix) = serializer.parent_namespaces.get(#default_namespace) {
|
false => serializer.write_start(None, #root_name, Some(#default_namespace))?,
|
||||||
serializer.output.write_str(prefix)?;
|
|
||||||
serializer.output.write_char(':')?;
|
|
||||||
serializer.output.write_str(field_context.name)?;
|
|
||||||
} else {
|
|
||||||
serializer.output.write_str(field_context.name)?;
|
|
||||||
serializer.output.write_str(" xmlns=\"")?;
|
|
||||||
serializer.output.write_str(#default_namespace)?;
|
|
||||||
serializer.output.write_char('\"')?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
serializer.output.write_str(field_context.name)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer.update_parent_default_namespace(#default_namespace);
|
serializer.update_parent_default_namespace(#default_namespace);
|
||||||
let mut to_remove: Vec<&str> = Vec::new();
|
let mut to_remove: Vec<&str> = Vec::new();
|
||||||
#prefixes
|
#prefixes
|
||||||
#attributes
|
#attributes
|
||||||
serializer.consume_current_attributes()?;
|
serializer.end_start()?;
|
||||||
serializer.output.write_char('>')?;
|
|
||||||
|
|
||||||
#body
|
#body
|
||||||
|
|
||||||
// Close tag
|
// Close tag
|
||||||
serializer.output.write_str("</")?;
|
serializer.write_close(None, #root_name)?;
|
||||||
serializer.output.write_str(#root_name)?;
|
|
||||||
serializer.output.write_char('>')?;
|
|
||||||
serializer.retrieve_parent_default_namespace();
|
serializer.retrieve_parent_default_namespace();
|
||||||
|
|
||||||
// Removing current namespaces
|
// Removing current namespaces
|
||||||
|
@ -102,6 +76,11 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element(::instant_xml::Id {
|
||||||
|
ns: #default_namespace,
|
||||||
|
name: #root_name,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -115,22 +94,10 @@ fn process_named_field(
|
||||||
let name = field.ident.as_ref().unwrap().to_string();
|
let name = field.ident.as_ref().unwrap().to_string();
|
||||||
let field_value = field.ident.as_ref().unwrap();
|
let field_value = field.ident.as_ref().unwrap();
|
||||||
|
|
||||||
let declaration = quote!(
|
|
||||||
let mut field = FieldContext {
|
|
||||||
name: #name,
|
|
||||||
attribute: None,
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
let field_meta = FieldMeta::from_field(field);
|
let field_meta = FieldMeta::from_field(field);
|
||||||
if field_meta.attribute {
|
if field_meta.attribute {
|
||||||
attributes.extend(quote!(
|
attributes.extend(quote!(
|
||||||
#declaration
|
serializer.write_attr(#name, &self.#field_value)?;
|
||||||
|
|
||||||
serializer.add_attribute_key(&#name)?;
|
|
||||||
field.attribute = Some(FieldAttribute::Attribute);
|
|
||||||
serializer.set_field_context(field)?;
|
|
||||||
self.#field_value.serialize(serializer)?;
|
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -143,13 +110,27 @@ fn process_named_field(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut no_lifetime_type = field.ty.clone();
|
||||||
|
discard_lifetimes(&mut no_lifetime_type);
|
||||||
body.extend(quote!(
|
body.extend(quote!(
|
||||||
#declaration
|
match <#no_lifetime_type as ToXml>::KIND {
|
||||||
match serializer.parent_namespaces.get(#ns) {
|
::instant_xml::Kind::Element(_) => {
|
||||||
Some(prefix) => field.attribute = Some(FieldAttribute::Prefix(prefix)),
|
|
||||||
None => field.attribute = Some(FieldAttribute::Namespace(#ns)),
|
|
||||||
}
|
|
||||||
serializer.set_field_context(field)?;
|
|
||||||
self.#field_value.serialize(serializer)?;
|
self.#field_value.serialize(serializer)?;
|
||||||
|
}
|
||||||
|
::instant_xml::Kind::Scalar => {
|
||||||
|
let (prefix, ns) = match serializer.parent_default_namespace() == #ns {
|
||||||
|
true => (None, None),
|
||||||
|
false => match serializer.parent_namespaces.get(#ns) {
|
||||||
|
Some(&prefix) => (Some(prefix), None),
|
||||||
|
None => (None, Some(#ns)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
serializer.write_start(prefix, #name, ns)?;
|
||||||
|
serializer.end_start()?;
|
||||||
|
self.#field_value.serialize(serializer)?;
|
||||||
|
serializer.write_close(prefix, #name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::{BTreeMap, VecDeque};
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
|
|
||||||
use super::Error;
|
use super::{Error, Id};
|
||||||
use xmlparser::{ElementEnd, Token, Tokenizer};
|
use xmlparser::{ElementEnd, Token, Tokenizer};
|
||||||
|
|
||||||
pub struct Deserializer<'cx, 'xml> {
|
pub struct Deserializer<'cx, 'xml> {
|
||||||
|
@ -321,23 +321,3 @@ pub struct Attribute<'xml> {
|
||||||
pub local: &'xml str,
|
pub local: &'xml str,
|
||||||
pub value: &'xml str,
|
pub value: &'xml str,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Kind {
|
|
||||||
Scalar,
|
|
||||||
Element(Id<'static>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Kind {
|
|
||||||
pub const fn name(&self, field: Id<'static>) -> Id<'static> {
|
|
||||||
match self {
|
|
||||||
Kind::Scalar => field,
|
|
||||||
Kind::Element(name) => *name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
||||||
pub struct Id<'a> {
|
|
||||||
pub ns: &'a str,
|
|
||||||
pub name: &'a str,
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,9 +2,7 @@ use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::de::Kind;
|
use crate::{Deserializer, Error, FromXml, Kind, Serializer, ToXml};
|
||||||
use crate::ser::FieldAttribute;
|
|
||||||
use crate::{Deserializer, Error, FromXml, Serializer, ToXml};
|
|
||||||
|
|
||||||
// Deserializer
|
// Deserializer
|
||||||
struct FromXmlStr<T: FromStr>(Option<T>);
|
struct FromXmlStr<T: FromStr>(Option<T>);
|
||||||
|
@ -42,23 +40,10 @@ where
|
||||||
&self,
|
&self,
|
||||||
serializer: &mut Serializer<W>,
|
serializer: &mut Serializer<W>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let field_context = match serializer.consume_field_context() {
|
serializer.write_str(self.0)
|
||||||
Some(field_context) => field_context,
|
}
|
||||||
None => return Err(Error::UnexpectedValue),
|
|
||||||
};
|
|
||||||
|
|
||||||
match field_context.attribute {
|
const KIND: Kind = Kind::Scalar;
|
||||||
Some(FieldAttribute::Attribute) => {
|
|
||||||
serializer.add_attribute_value(&self.0)?;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
serializer.add_open_tag(&field_context)?;
|
|
||||||
write!(serializer.output, "{}", self.0)?;
|
|
||||||
serializer.add_close_tag(field_context)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! to_xml_for_number {
|
macro_rules! to_xml_for_number {
|
||||||
|
@ -70,6 +55,8 @@ macro_rules! to_xml_for_number {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
DisplayToXml(self).serialize(serializer)
|
DisplayToXml(self).serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KIND: Kind = DisplayToXml::<Self>::KIND;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -186,6 +173,8 @@ impl ToXml for bool {
|
||||||
|
|
||||||
DisplayToXml(&value).serialize(serializer)
|
DisplayToXml(&value).serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KIND: Kind = DisplayToXml::<Self>::KIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToXml for String {
|
impl ToXml for String {
|
||||||
|
@ -195,6 +184,8 @@ impl ToXml for String {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
DisplayToXml(&encode(self)?).serialize(serializer)
|
DisplayToXml(&encode(self)?).serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KIND: Kind = DisplayToXml::<Self>::KIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToXml for char {
|
impl ToXml for char {
|
||||||
|
@ -205,6 +196,8 @@ impl ToXml for char {
|
||||||
let mut tmp = [0u8; 4];
|
let mut tmp = [0u8; 4];
|
||||||
DisplayToXml(&encode(&*self.encode_utf8(&mut tmp))?).serialize(serializer)
|
DisplayToXml(&encode(&*self.encode_utf8(&mut tmp))?).serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KIND: Kind = DisplayToXml::<Self>::KIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToXml for &str {
|
impl ToXml for &str {
|
||||||
|
@ -214,6 +207,8 @@ impl ToXml for &str {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
DisplayToXml(&encode(self)?).serialize(serializer)
|
DisplayToXml(&encode(self)?).serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KIND: Kind = DisplayToXml::<Self>::KIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToXml for Cow<'_, str> {
|
impl ToXml for Cow<'_, str> {
|
||||||
|
@ -223,6 +218,8 @@ impl ToXml for Cow<'_, str> {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
DisplayToXml(&encode(self)?).serialize(serializer)
|
DisplayToXml(&encode(self)?).serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KIND: Kind = DisplayToXml::<Self>::KIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ToXml> ToXml for Option<T> {
|
impl<T: ToXml> ToXml for Option<T> {
|
||||||
|
@ -235,6 +232,8 @@ impl<T: ToXml> ToXml for Option<T> {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KIND: Kind = T::KIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode(input: &str) -> Result<Cow<'_, str>, Error> {
|
fn encode(input: &str) -> Result<Cow<'_, str>, Error> {
|
||||||
|
|
|
@ -7,8 +7,8 @@ pub use macros::{FromXml, ToXml};
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod de;
|
pub mod de;
|
||||||
mod impls;
|
mod impls;
|
||||||
|
use de::Context;
|
||||||
pub use de::Deserializer;
|
pub use de::Deserializer;
|
||||||
use de::{Context, Kind};
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod ser;
|
pub mod ser;
|
||||||
pub use ser::Serializer;
|
pub use ser::Serializer;
|
||||||
|
@ -18,6 +18,8 @@ pub trait ToXml {
|
||||||
&self,
|
&self,
|
||||||
serializer: &mut Serializer<W>,
|
serializer: &mut Serializer<W>,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
|
const KIND: Kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromXml<'xml>: Sized {
|
pub trait FromXml<'xml>: Sized {
|
||||||
|
@ -93,3 +95,23 @@ pub enum Error {
|
||||||
#[error("duplicate value")]
|
#[error("duplicate value")]
|
||||||
DuplicateValue,
|
DuplicateValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum Kind {
|
||||||
|
Scalar,
|
||||||
|
Element(Id<'static>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Kind {
|
||||||
|
pub const fn name(&self, field: Id<'static>) -> Id<'static> {
|
||||||
|
match self {
|
||||||
|
Kind::Scalar => field,
|
||||||
|
Kind::Element(name) => *name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct Id<'a> {
|
||||||
|
pub ns: &'a str,
|
||||||
|
pub name: &'a str,
|
||||||
|
}
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self};
|
||||||
|
|
||||||
use super::Error;
|
use super::Error;
|
||||||
|
use crate::ToXml;
|
||||||
|
|
||||||
pub struct Serializer<'xml, W: fmt::Write + ?Sized> {
|
pub struct Serializer<'xml, W: fmt::Write + ?Sized> {
|
||||||
// For parent namespaces the key is the namespace and the value is the prefix. We are adding to map
|
// 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.
|
// only if the namespaces do not exist, if it does exist then we are using an already defined parent prefix.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub parent_namespaces: HashMap<&'xml str, &'xml str>,
|
pub parent_namespaces: HashMap<&'xml str, &'xml str>,
|
||||||
#[doc(hidden)]
|
output: &'xml mut W,
|
||||||
pub output: &'xml mut W,
|
|
||||||
|
|
||||||
parent_default_namespace: &'xml str,
|
parent_default_namespace: &'xml str,
|
||||||
parent_default_namespace_to_revert: &'xml str,
|
parent_default_namespace_to_revert: &'xml str,
|
||||||
current_attributes: String,
|
state: State,
|
||||||
next_field_context: Option<FieldContext<'xml>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'xml, W: fmt::Write + ?Sized> Serializer<'xml, W> {
|
impl<'xml, W: fmt::Write + ?Sized> Serializer<'xml, W> {
|
||||||
|
@ -24,42 +22,88 @@ impl<'xml, W: fmt::Write + ?Sized> Serializer<'xml, W> {
|
||||||
output,
|
output,
|
||||||
parent_default_namespace: "",
|
parent_default_namespace: "",
|
||||||
parent_default_namespace_to_revert: "",
|
parent_default_namespace_to_revert: "",
|
||||||
next_field_context: None,
|
state: State::Element,
|
||||||
current_attributes: String::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume_current_attributes(&mut self) -> Result<(), Error> {
|
pub fn write_start(
|
||||||
self.output.write_str(&self.current_attributes)?;
|
&mut self,
|
||||||
self.current_attributes.clear();
|
prefix: Option<&str>,
|
||||||
Ok(())
|
name: &str,
|
||||||
}
|
ns: Option<&str>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
pub fn add_attribute_key(&mut self, attr_key: &impl fmt::Display) -> Result<(), Error> {
|
if self.state != State::Element {
|
||||||
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);
|
return Err(Error::UnexpectedState);
|
||||||
};
|
}
|
||||||
|
|
||||||
self.next_field_context = Some(field_context);
|
match prefix {
|
||||||
|
Some(prefix) => self.output.write_fmt(format_args!("<{prefix}:{name}"))?,
|
||||||
|
None => match ns {
|
||||||
|
Some(ns) => self
|
||||||
|
.output
|
||||||
|
.write_fmt(format_args!("<{name} xmlns=\"{ns}\""))?,
|
||||||
|
None => self.output.write_fmt(format_args!("<{name}"))?,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state = State::Attribute;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume_field_context(&mut self) -> Option<FieldContext<'xml>> {
|
pub fn write_attr<V: ToXml + ?Sized>(&mut self, name: &str, value: &V) -> Result<(), Error> {
|
||||||
self.next_field_context.take()
|
if self.state != State::Attribute {
|
||||||
|
return Err(Error::UnexpectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output.write_fmt(format_args!(" {}=\"", name))?;
|
||||||
|
self.state = State::Scalar;
|
||||||
|
value.serialize(self)?;
|
||||||
|
self.state = State::Attribute;
|
||||||
|
self.output.write_char('"')?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_prefix(&mut self, prefix: &str, ns: &str) -> Result<(), Error> {
|
||||||
|
if self.state != State::Attribute {
|
||||||
|
return Err(Error::UnexpectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output
|
||||||
|
.write_fmt(format_args!(" xmlns:{prefix}=\"{ns}\""))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_str<V: fmt::Display + ?Sized>(&mut self, value: &V) -> Result<(), Error> {
|
||||||
|
if !matches!(self.state, State::Element | State::Scalar) {
|
||||||
|
return Err(Error::UnexpectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output.write_fmt(format_args!("{}", value))?;
|
||||||
|
self.state = State::Element;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_start(&mut self) -> Result<(), Error> {
|
||||||
|
if self.state != State::Attribute {
|
||||||
|
return Err(Error::UnexpectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output.write_char('>')?;
|
||||||
|
self.state = State::Element;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_close(&mut self, prefix: Option<&str>, name: &str) -> Result<(), Error> {
|
||||||
|
if self.state != State::Element {
|
||||||
|
return Err(Error::UnexpectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
match prefix {
|
||||||
|
Some(prefix) => self.output.write_fmt(format_args!("</{prefix}:{name}>"))?,
|
||||||
|
None => self.output.write_fmt(format_args!("</{name}>"))?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_parent_default_namespace(&mut self, namespace: &'xml str) -> Result<(), Error> {
|
pub fn set_parent_default_namespace(&mut self, namespace: &'xml str) -> Result<(), Error> {
|
||||||
|
@ -79,62 +123,11 @@ impl<'xml, W: fmt::Write + ?Sized> Serializer<'xml, W> {
|
||||||
pub fn retrieve_parent_default_namespace(&mut self) {
|
pub fn retrieve_parent_default_namespace(&mut self) {
|
||||||
self.parent_default_namespace = self.parent_default_namespace_to_revert;
|
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(prefix)?;
|
|
||||||
self.output.write_char(':')?;
|
|
||||||
self.output.write_str(field_context.name)?;
|
|
||||||
self.output.write_char('>')?;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.output.write_str("</")?;
|
|
||||||
self.output.write_str(field_context.name)?;
|
|
||||||
self.output.write_char('>')?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FieldContext<'xml> {
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
#[doc(hidden)]
|
enum State {
|
||||||
pub name: &'xml str,
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub attribute: Option<FieldAttribute<'xml>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum FieldAttribute<'xml> {
|
|
||||||
Prefix(&'xml str),
|
|
||||||
Namespace(&'xml str),
|
|
||||||
Attribute,
|
Attribute,
|
||||||
|
Element,
|
||||||
|
Scalar,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue