use super::super::{DEFAULT_FN, SEMANTIC_DE_FN, SEMANTIC_SER_FN};
use common::{Exists, FieldType};
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 StructField {
ty: syn::Type,
name: String,
start_version: u16,
end_version: u16,
attrs: HashMap<String, syn::Lit>,
}
impl Exists for StructField {
fn start_version(&self) -> u16 {
self.start_version
}
fn end_version(&self) -> u16 {
self.end_version
}
}
impl FieldType for StructField {
fn ty(&self) -> syn::Type {
self.ty.clone()
}
}
impl StructField {
pub fn new(
base_version: u16,
ast_field: syn::punctuated::Pair<&syn::Field, &syn::token::Comma>,
) -> Self {
let attrs = parse_field_attributes(&ast_field.value().attrs);
StructField {
ty: ast_field.value().ty.clone(),
name: ast_field.value().ident.as_ref().unwrap().to_string(),
start_version: get_start_version(&attrs).unwrap_or(base_version),
end_version: get_end_version(&attrs).unwrap_or_default(),
attrs,
}
}
pub fn generate_semantic_serializer(&self, target_version: u16) -> proc_macro2::TokenStream {
if !self.exists_at(target_version) {
if let Some(semantic_ser_fn) = get_ident_attr(&self.attrs, SEMANTIC_SER_FN) {
return quote! {
copy_of_self.#semantic_ser_fn(version)?;
};
}
}
quote! {}
}
pub fn generate_semantic_deserializer(&self, source_version: u16) -> proc_macro2::TokenStream {
if !self.exists_at(source_version) {
if let Some(semantic_de_fn) = get_ident_attr(&self.attrs, SEMANTIC_DE_FN) {
return quote! {
object.#semantic_de_fn(version)?;
};
}
}
quote! {}
}
pub fn generate_serializer(&self, target_version: u16) -> proc_macro2::TokenStream {
let field_ident = format_ident!("{}", self.name);
if !self.exists_at(target_version) {
return proc_macro2::TokenStream::new();
}
match &self.ty {
syn::Type::Array(_) => quote! {
for element in copy_of_self.#field_ident.iter() {
Versionize::serialize(element, writer, version_map, app_version)?;
}
},
syn::Type::Path(_) => quote! {
Versionize::serialize(©_of_self.#field_ident, writer, version_map, app_version)?;
},
syn::Type::Reference(_) => quote! {
copy_of_self.#field_ident.serialize(writer, version_map, app_version)?;
},
_ => panic!("Unsupported field type {:?}", self.ty),
}
}
pub fn generate_deserializer(&self, source_version: u16) -> proc_macro2::TokenStream {
let field_ident = format_ident!("{}", self.name);
if !self.exists_at(source_version) {
if let Some(default_fn) = get_ident_attr(&self.attrs, DEFAULT_FN) {
return quote! {
#field_ident: Self::#default_fn(version),
};
} else {
return quote! { #field_ident: Default::default(), };
}
}
let ty = &self.ty;
match ty {
syn::Type::Array(array) => {
let array_type_token;
match *array.elem.clone() {
syn::Type::Path(token) => {
array_type_token = token;
}
_ => panic!("Unsupported array type."),
}
self.generate_array_deserializer(array_type_token, &array.len)
}
syn::Type::Path(_) => quote! {
#field_ident: <#ty as Versionize>::deserialize(&mut reader, version_map, app_version)?,
},
syn::Type::Reference(_) => quote! {
#field_ident: <#ty as Versionize>::deserialize(&mut reader, version_map, app_version)?,
},
_ => panic!("Unsupported field type {:?}", self.ty),
}
}
fn generate_array_deserializer(
&self,
array_type_token: syn::TypePath,
array_len: &syn::Expr,
) -> proc_macro2::TokenStream {
let field_ident = format_ident!("{}", self.name);
quote! {
#field_ident: {
let mut array = [#array_type_token::default() ; #array_len];
for i in 0..#array_len {
array[i] = <#array_type_token as Versionize>::deserialize(&mut reader, version_map, app_version)?;
}
array
},
}
}
}