use super::super::DEFAULT_FN;
use common::Exists;
use helpers::{get_end_version, get_ident_attr, get_start_version, parse_field_attributes};
use quote::{format_ident, quote};
use std::collections::hash_map::HashMap;
#[derive(Debug, Eq, PartialEq, Clone)]
pub(crate) struct EnumVariant {
ident: syn::Ident,
ty: Vec<syn::Type>,
start_version: u16,
variant_index: u32,
end_version: u16,
attrs: HashMap<String, syn::Lit>,
}
impl Exists for EnumVariant {
fn start_version(&self) -> u16 {
self.start_version
}
fn end_version(&self) -> u16 {
self.end_version
}
}
impl EnumVariant {
pub fn new(base_version: u16, ast_variant: &syn::Variant, variant_index: u32) -> Self {
let attrs = parse_field_attributes(&ast_variant.attrs);
let ty = match &ast_variant.fields {
syn::Fields::Unnamed(fields) => fields
.unnamed
.iter()
.map(|field| field.ty.clone())
.collect(),
_ => Vec::new(),
};
EnumVariant {
ident: ast_variant.ident.clone(),
ty,
variant_index,
start_version: get_start_version(&attrs).unwrap_or(base_version),
end_version: get_end_version(&attrs).unwrap_or_default(),
attrs,
}
}
pub fn generate_serializer(&self, target_version: u16) -> proc_macro2::TokenStream {
let field_ident = &self.ident;
let variant_index = self.variant_index;
if !self.exists_at(target_version) {
if let Some(default_fn_ident) = get_ident_attr(&self.attrs, DEFAULT_FN) {
let field_type_ident = if self.ty.is_empty() {
quote! { Self::#field_ident => }
} else {
quote! { Self::#field_ident(..) => }
};
let mut serializer = proc_macro2::TokenStream::new();
serializer.extend(field_type_ident);
serializer.extend(self.default_fn_serializer(default_fn_ident));
return serializer;
} else {
panic!("Variant {} does not exist in version {}, please implement a default_fn function that provides a default value for this variant.", field_ident, target_version);
}
}
let mut serialize_data = proc_macro2::TokenStream::new();
let mut data_tuple = proc_macro2::TokenStream::new();
for (index, _) in self.ty.iter().enumerate() {
let data_ident = format_ident!("data_{}", index);
data_tuple.extend(quote!(#data_ident,));
serialize_data.extend(quote! {
Versionize::serialize(#data_ident, writer, version_map, app_version)?;
});
}
if self.ty.is_empty() {
quote! {
Self::#field_ident => {
let index: u32 = #variant_index;
Versionize::serialize(&index, writer, version_map, app_version)?;
},
}
} else {
quote! {
Self::#field_ident(#data_tuple) => {
let index: u32 = #variant_index;
Versionize::serialize(&index, writer, version_map, app_version)?;
#serialize_data
},
}
}
}
pub fn generate_deserializer(&self) -> proc_macro2::TokenStream {
let variant_index = self.variant_index;
let ident = &self.ident;
if self.ty.is_empty() {
return quote! {
#variant_index => {
return Ok(Self::#ident);
},
};
}
let mut deserialize_data = proc_macro2::TokenStream::new();
let mut data_tuple = proc_macro2::TokenStream::new();
for (index, data_type) in self.ty.iter().enumerate() {
let data_ident = format_ident!("data_{}", index);
data_tuple.extend(quote!(#data_ident,));
deserialize_data.extend(
quote! {
let #data_ident = <#data_type as Versionize>::deserialize(&mut reader, version_map, app_version)?;
}
);
}
quote! {
#variant_index => {
#deserialize_data
return Ok(Self::#ident(#data_tuple));
},
}
}
fn default_fn_serializer(&self, default_fn_ident: syn::Ident) -> proc_macro2::TokenStream {
quote! {
{
let new_variant = self.#default_fn_ident(version)?;
new_variant.serialize(writer, version_map, app_version)?;
},
}
}
}