// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 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, start_version: u16, // Bincode uses u32 instead of usize also. variant_index: u32, end_version: u16, attrs: HashMap, } 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, // Set base version. start_version: get_start_version(&attrs).unwrap_or(base_version), end_version: get_end_version(&attrs).unwrap_or_default(), attrs, } } // Emits code that serializes an enum variant. 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; // Enum variant with no data. 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! { { // Call user defined fn to provide a variant that exists in target version. let new_variant = self.#default_fn_ident(version)?; // The new_variant will serialize its index and data. new_variant.serialize(writer, version_map, app_version)?; }, } } }