instant-xml/instant-xml-macros/src/ser.rs

319 lines
10 KiB
Rust
Raw Normal View History

use proc_macro2::TokenStream;
2022-09-27 16:23:28 +00:00
use quote::quote;
use syn::spanned::Spanned;
use super::{discard_lifetimes, meta_items, ContainerMeta, FieldMeta, Mode, VariantMeta};
use crate::{case::RenameRule, Namespace};
2022-09-05 11:14:31 +00:00
pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
2022-09-26 10:55:16 +00:00
let meta = match ContainerMeta::from_derive(input) {
Ok(meta) => meta,
Err(e) => return e.to_compile_error(),
};
match (&input.data, meta.mode) {
(syn::Data::Struct(data), None) => serialize_struct(input, data, meta),
(syn::Data::Enum(data), Some(Mode::Scalar)) => serialize_scalar_enum(input, data, meta),
(syn::Data::Enum(data), Some(Mode::Wrapped)) => serialize_wrapped_enum(input, data, meta),
(syn::Data::Struct(_), _) => {
2022-11-23 04:39:11 +00:00
syn::Error::new(input.span(), "enum mode not allowed on struct type").to_compile_error()
2022-09-22 09:57:04 +00:00
}
(syn::Data::Enum(_), _) => {
syn::Error::new(input.span(), "missing enum mode").to_compile_error()
2022-09-22 09:57:04 +00:00
}
2022-09-05 11:14:31 +00:00
_ => todo!(),
}
}
fn serialize_scalar_enum(
2022-09-22 09:57:04 +00:00
input: &syn::DeriveInput,
data: &syn::DataEnum,
2022-11-19 19:20:14 +00:00
meta: ContainerMeta,
2022-09-22 09:57:04 +00:00
) -> TokenStream {
let ident = &input.ident;
let mut variants = TokenStream::new();
for variant in data.variants.iter() {
let meta = match VariantMeta::from_variant(variant, &meta) {
2022-11-19 19:20:14 +00:00
Ok(meta) => meta,
Err(err) => return err.to_compile_error(),
};
2022-09-22 09:57:04 +00:00
let v_ident = &variant.ident;
2022-09-22 09:57:04 +00:00
let serialize_as = meta.serialize_as;
variants.extend(quote!(#ident::#v_ident => #serialize_as,));
}
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
quote!(
impl #impl_generics ToXml for #ident #ty_generics #where_clause {
fn serialize<W: ::core::fmt::Write + ?::core::marker::Sized>(
&self,
serializer: &mut instant_xml::Serializer<W>,
) -> Result<(), instant_xml::Error> {
2022-11-19 19:20:14 +00:00
serializer.write_str(match self { #variants })
2022-09-22 09:57:04 +00:00
}
2022-11-23 18:32:43 +00:00
const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Scalar;
2022-11-19 19:20:14 +00:00
}
2022-09-22 09:57:04 +00:00
)
}
fn serialize_wrapped_enum(
input: &syn::DeriveInput,
data: &syn::DataEnum,
meta: ContainerMeta,
) -> TokenStream {
if meta.rename_all != RenameRule::None {
return syn::Error::new(
input.span(),
"rename_all is not allowed on wrapped enum type",
)
.to_compile_error();
}
let ident = &input.ident;
let mut variants = TokenStream::new();
for variant in data.variants.iter() {
match &variant.fields {
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {}
_ => {
return syn::Error::new(
input.span(),
"wrapped enum variants must have 1 unnamed field",
)
.to_compile_error()
}
}
if !meta_items(&variant.attrs).is_empty() {
return syn::Error::new(
input.span(),
"attributes not allowed on wrapped enum variants",
)
.to_compile_error();
}
let v_ident = &variant.ident;
variants.extend(quote!(#ident::#v_ident(inner) => inner.serialize(serializer)?,));
}
let default_namespace = meta.default_namespace();
let cx_len = meta.ns.prefixes.len();
let mut context = quote!(
let mut new = ::instant_xml::ser::Context::<#cx_len>::default();
new.default_ns = #default_namespace;
);
for (i, (prefix, ns)) in meta.ns.prefixes.iter().enumerate() {
context.extend(quote!(
new.prefixes[#i] = ::instant_xml::ser::Prefix { ns: #ns, prefix: #prefix };
));
}
let mut generics = input.generics.clone();
for param in generics.type_params_mut() {
param
.bounds
.push(syn::parse_str("::instant_xml::ToXml").unwrap());
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let tag = meta.tag();
quote!(
impl #impl_generics ToXml for #ident #ty_generics #where_clause {
fn serialize<W: ::core::fmt::Write + ?::core::marker::Sized>(
&self,
serializer: &mut instant_xml::Serializer<W>,
) -> Result<(), instant_xml::Error> {
// Start tag
let prefix = serializer.write_start(#tag, #default_namespace, false)?;
debug_assert_eq!(prefix, None);
// Set up element context, this will also emit namespace declarations
#context
let old = serializer.push(new)?;
// Finalize start element
serializer.end_start()?;
match self {
#variants
}
// Close tag
serializer.write_close(prefix, #tag)?;
serializer.pop(old);
Ok(())
}
const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Element(::instant_xml::Id {
ns: #default_namespace,
name: #tag,
});
};
)
}
fn serialize_struct(
input: &syn::DeriveInput,
data: &syn::DataStruct,
meta: ContainerMeta,
) -> proc_macro2::TokenStream {
let mut body = TokenStream::new();
let mut attributes = TokenStream::new();
match data.fields {
syn::Fields::Named(ref fields) => {
fields.named.iter().for_each(|field| {
process_named_field(field, &mut body, &mut attributes, &meta);
});
}
syn::Fields::Unnamed(_) => todo!(),
syn::Fields::Unit => {}
2022-09-05 11:14:31 +00:00
};
2022-09-27 16:23:28 +00:00
let default_namespace = meta.default_namespace();
let cx_len = meta.ns.prefixes.len();
let mut context = quote!(
let mut new = ::instant_xml::ser::Context::<#cx_len>::default();
new.default_ns = #default_namespace;
);
2022-09-27 16:23:28 +00:00
for (i, (prefix, ns)) in meta.ns.prefixes.iter().enumerate() {
context.extend(quote!(
new.prefixes[#i] = ::instant_xml::ser::Prefix { ns: #ns, prefix: #prefix };
));
}
2022-09-05 11:14:31 +00:00
2022-11-22 04:33:11 +00:00
let mut generics = input.generics.clone();
for param in generics.type_params_mut() {
param
.bounds
.push(syn::parse_str("::instant_xml::ToXml").unwrap());
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
2022-09-27 16:23:28 +00:00
let tag = meta.tag();
let ident = &input.ident;
2022-09-05 11:14:31 +00:00
quote!(
impl #impl_generics ToXml for #ident #ty_generics #where_clause {
2022-09-05 11:14:31 +00:00
fn serialize<W: ::core::fmt::Write + ?::core::marker::Sized>(
&self,
serializer: &mut instant_xml::Serializer<W>,
) -> Result<(), instant_xml::Error> {
// Start tag
2022-09-07 20:32:24 +00:00
let prefix = serializer.write_start(#tag, #default_namespace, false)?;
debug_assert_eq!(prefix, None);
// Set up element context, this will also emit namespace declarations
#context
let old = serializer.push(new)?;
// Finalize start element
2022-09-05 11:14:31 +00:00
#attributes
serializer.end_start()?;
2022-09-05 11:14:31 +00:00
#body
// Close tag
2022-09-07 20:32:24 +00:00
serializer.write_close(prefix, #tag)?;
serializer.pop(old);
2022-09-05 11:14:31 +00:00
Ok(())
}
2022-11-23 18:32:43 +00:00
const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Element(::instant_xml::Id {
ns: #default_namespace,
2022-09-07 20:32:24 +00:00
name: #tag,
});
2022-09-05 11:14:31 +00:00
};
)
}
fn process_named_field(
field: &syn::Field,
body: &mut TokenStream,
attributes: &mut TokenStream,
meta: &ContainerMeta,
) {
2022-09-07 20:32:24 +00:00
let field_name = field.ident.as_ref().unwrap();
2022-09-27 16:23:28 +00:00
let field_meta = match FieldMeta::from_field(field, meta) {
Ok(meta) => meta,
Err(err) => {
body.extend(err.into_compile_error());
return;
}
};
2022-09-27 16:23:28 +00:00
let tag = field_meta.tag;
let default_ns = match &meta.ns.uri {
Some(ns) => quote!(#ns),
None => quote!(""),
2022-09-07 20:32:24 +00:00
};
if field_meta.attribute {
let (ns, error) = match &field_meta.ns.uri {
Some(Namespace::Path(path)) => match path.get_ident() {
Some(prefix) => match &meta.ns.prefixes.get(&prefix.to_string()) {
Some(ns) => (quote!(#ns), quote!()),
None => (
quote!(""),
syn::Error::new(
field_meta.ns.uri.span(),
&format!("unknown prefix `{prefix}` (prefix must be defined on the field's type)"),
)
.into_compile_error(),
),
},
None => (
quote!(""),
syn::Error::new(
field_meta.ns.uri.span(),
"attribute namespace must be a prefix identifier",
)
.into_compile_error(),
),
},
Some(Namespace::Literal(_)) => (
quote!(""),
syn::Error::new(
field_meta.ns.uri.span(),
"attribute namespace must be a prefix identifier",
)
.into_compile_error(),
),
2022-09-27 16:23:28 +00:00
None => (default_ns, quote!()),
};
attributes.extend(quote!(
#error
2022-09-07 20:32:24 +00:00
serializer.write_attr(#tag, #ns, &self.#field_name)?;
));
return;
}
let ns = match field_meta.ns.uri {
2022-09-27 16:23:28 +00:00
Some(ref ns) => quote!(#ns),
None => default_ns,
};
let mut no_lifetime_type = field.ty.clone();
discard_lifetimes(&mut no_lifetime_type);
body.extend(quote!(
match <#no_lifetime_type as ToXml>::KIND {
::instant_xml::Kind::Element(_) => {
2022-09-07 20:32:24 +00:00
self.#field_name.serialize(serializer)?;
}
::instant_xml::Kind::Scalar | ::instant_xml::Kind::Vec => {
2022-09-07 20:32:24 +00:00
let prefix = serializer.write_start(#tag, #ns, true)?;
serializer.end_start()?;
2022-09-07 20:32:24 +00:00
self.#field_name.serialize(serializer)?;
serializer.write_close(prefix, #tag)?;
}
}
));
}