use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned}; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::{ parse_macro_input, Attribute, Data, DeriveInput, Field, Fields, Ident, Index, Meta, MetaList, Token, Type, Variant, }; /// Derive the type Serializable. /// /// For simple case, it is just a concatenation of the Serializable fields. /// /// ## Until /// /// For list of data, we can serialize data until we find some specific data. To do so we need /// the type of the repeated data `D`, the type of the symbole at the end of the list `U`, and /// the specific value of the symbole at the end of the list `end`. /// /// For example, we can deserialize a `Vec` until we find `0x0000`: /// /// `[0, 1, 2, 3, 0, 0]` would be serialized as `vec![0, 1, 2, 3]` /// /// In this example, `D` is `u8`, `U` is `u16` and `end` is `0x0000`. /// /// To define a field using this method, the type of field must implement the trait /// `SerializableUntil`, `D` and `U` must implement `Serializable`, and the field /// must be marked with the attribute `#[until(D, U, end)]`: /// /// ``` /// pub use androscalpel_serializer::*; /// /// #[derive(Serializable, PartialEq, Debug)] /// struct Example { /// #[until(u8, u16, 0)] /// a: Vec, /// } /// /// assert_eq!( /// Example::deserialize_from_slice(&[0, 1, 2, 3, 0, 0]).unwrap(), /// Example { a: vec![0, 1, 2, 3] } /// ); /// ``` /// /// ## Enums /// /// Enums are a litle tricky. /// To distinguish the enum variant, there binary representation must be prefixed by /// the serialization of a specific type. This type is the same for the whole enum, /// and is defined with `prefix_type`, e.g. `#[prefix_type(u8)]`. /// /// Each variant (except the default one) must define the prefix they match with `prefix`, /// e.g. `#[prefix(0x42)]`. /// /// An enum can define ONE variant as the default variant. This variant is a catch all, /// and MUST be have named field or unnamed field (no unit variant!) and: /// - The first field of named fields variant must be named `prefix` and of the type /// `prefix_type`. /// - The first field of unnamed fields variant must be of the type `prefix_type`. /// /// The first field of the default variant store the prefix of the variant. /// /// ``` /// pub use androscalpel_serializer::*; /// /// #[derive(Serializable, PartialEq, Debug)] /// #[prefix_type(u8)] /// enum Example { /// #[prefix(0)] /// Zero, /// #[prefix(1)] /// One(u32), /// #[prefix(2)] /// Two { a: u16, b: u32 }, /// #[default_variant] /// Dft(u8, u32) /// } /// /// assert_eq!( /// Example::deserialize_from_slice(&[0u8]).unwrap(), /// Example::Zero /// ); /// assert_eq!( /// Example::deserialize_from_slice(&[1u8, 0x67, 0x45, 0x23u8, 0x01u8]).unwrap(), /// Example::One(0x01234567) /// ); /// assert_eq!( /// Example::deserialize_from_slice(&[2u8, 0xDCu8, 0xFEu8, 0x67, 0x45, 0x23u8, 0x01u8]).unwrap(), /// Example::Two { a: 0xFEDC, b: 0x01234567 } /// ); /// assert_eq!( /// Example::deserialize_from_slice(&[42u8, 0x67, 0x45, 0x23u8, 0x01u8]).unwrap(), /// Example::Dft(42, 0x01234567) /// ); /// ``` /// /// ## Prefix and suffix /// /// To define a constant prefix before a serialised field or suffixe after, use `#[prefix()]` /// and `#[suffix()]`. The prefix/suffix must be `u8`s `Iterator`. /// /// ``` /// pub use androscalpel_serializer::*; /// /// #[derive(Serializable, PartialEq, Debug)] /// struct Example { /// #[prefix([0x42])] /// #[suffix([0x66, 0x66])] /// a: u32, /// } /// /// assert_eq!( /// Example::deserialize_from_slice(&[0x42, 3, 2, 1, 0, 0x66, 0x66]).unwrap(), /// Example { a: 0x00010203 } /// ); /// ``` /// /// #[proc_macro_derive( Serializable, attributes(until, prefix, prefix_type, suffix, default_variant) )] pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let name = input.ident; let params = ParamsStruct::parse(&input.attrs); let implem_serialize = get_implem_serialize(&input.data, ¶ms); let implem_deserialize = get_implem_deserialize(&input.data, ¶ms); let implem_size = get_implem_size(&input.data, ¶ms); let expanded = quote! { impl androscalpel_serializer::Serializable for #name { #[allow(clippy::single_element_loop, clippy::let_and_return)] fn serialize(&self, output: &mut dyn std::io::Write) -> androscalpel_serializer::Result<()> { #implem_serialize } #[allow(clippy::single_element_loop, clippy::let_and_return, clippy::needless_return)] fn deserialize(input: &mut dyn androscalpel_serializer::ReadSeek) -> androscalpel_serializer::Result { #implem_deserialize } #[allow(clippy::identity_op)] fn size(&self) -> usize { #implem_size } } }; proc_macro::TokenStream::from(expanded) } /// Parsed Parameters for the `#[until(D, U, end)]` attribute. /// `D` is the type of the repeated data, `U` is the type of `end`, /// `end` is the object that mark the end of the repetition of `U`s. struct UntilParams(Ident, Ident, TokenStream); impl Parse for UntilParams { fn parse(input: ParseStream) -> syn::Result { let data_type = input.parse()?; input.parse::()?; let end_type = input.parse()?; input.parse::()?; let until = input.parse()?; Ok(Self(data_type, end_type, until)) } } /// Parsed Parameters for the `#[prefix(val)]` attribute of an enum variant. struct PrefixParamsVariant(TokenStream); impl Parse for PrefixParamsVariant { fn parse(input: ParseStream) -> syn::Result { let value = input.parse()?; Ok(Self(value)) } } /// Parsed Parameters for the `#[prefix_type(T)]` attribute. struct PrefixTypeParams(TokenStream); impl Parse for PrefixTypeParams { fn parse(input: ParseStream) -> syn::Result { let data_type = input.parse()?; Ok(Self(data_type)) } } /// All the attributes parameters for a struct/enum #[derive(Default)] struct ParamsStruct { pub prefix_type: Option, } /// Parsed Parameters for the `#[prefix(val)]` attribute of an struct field. struct PrefixParamsField(TokenStream); impl Parse for PrefixParamsField { fn parse(input: ParseStream) -> syn::Result { let value = input.parse()?; Ok(Self(value)) } } /// Parsed Parameters for the `#[suffix(val)]` attribute of an struct field. struct SuffixParamsField(TokenStream); impl Parse for SuffixParamsField { fn parse(input: ParseStream) -> syn::Result { let value = input.parse()?; Ok(Self(value)) } } /// All the attributes parameters for a field #[derive(Default)] struct ParamsField { pub until: Option, pub prefix: Option, pub suffix: Option, } /// All the attributes parameters for a variant #[derive(Default)] struct ParamsVariant { pub prefix: Option, pub default_variant: Option<()>, } impl ParamsStruct { fn parse(attrs: &[Attribute]) -> Self { let mut params = ParamsStruct::default(); for attr in attrs { match &attr.meta { Meta::List(MetaList { path, .. }) if path.is_ident("until") => { panic!("Structur/Enum cannot take the attribut 'until'") } Meta::List(MetaList { path, .. }) if path.is_ident("prefix") => { panic!("Structur/Enum cannot take the attribut 'prefix'") } Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix_type") => { params.prefix_type = Some(syn::parse2(tokens.clone()).unwrap()); } _ => (), } } params } } impl ParamsField { fn parse(attrs: &[Attribute]) -> Self { let mut params = ParamsField::default(); for attr in attrs { match &attr.meta { Meta::List(MetaList { path, tokens, .. }) if path.is_ident("until") => { params.until = Some(syn::parse2(tokens.clone()).unwrap()) } Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix") => { params.prefix = Some(syn::parse2(tokens.clone()).unwrap()) } Meta::List(MetaList { path, tokens, .. }) if path.is_ident("suffix") => { params.suffix = Some(syn::parse2(tokens.clone()).unwrap()) } Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => { panic!("Fields cannot take the attribut 'prefix_type'") } _ => (), } } params } } impl ParamsVariant { fn parse(attrs: &[Attribute]) -> Self { let mut params = ParamsVariant::default(); for attr in attrs { match &attr.meta { Meta::List(MetaList { path, .. }) if path.is_ident("until") => { panic!("Variant cannot take the attribut 'until'") } Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix") => { params.prefix = Some(syn::parse2(tokens.clone()).unwrap()); } Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => { panic!("Variant cannot take the attribut 'prefix_type'") } Meta::Path(path) if path.is_ident("default_variant") => { params.default_variant = Some(()); } _ => (), } } params } } /// Return the match statement an enum variant. fn get_enum_match(variant: &Variant) -> TokenStream { match variant.fields { Fields::Named(ref fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; quote! { #name } }); quote_spanned! { variant.span() => {#(#recurse,)*} } } Fields::Unnamed(ref fields) => { let names: Vec<_> = fields .unnamed .iter() .enumerate() .map(|(i, _)| format_ident!("field{}", i)) .collect(); quote_spanned! { variant.span() => (#(#names,)*) } } Fields::Unit => quote! {}, } } /// Return the implementation of the computation of [`androscalpel_serializer::Serializable::size`] /// for a specific field `f` accessible using `field_ref`. fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream { let params = ParamsField::parse(&f.attrs); let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix { quote_spanned! { f.span() => #stream.iter().collect::>().len() } } else { quote_spanned! { f.span() => 0 } }; let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix { quote_spanned! { f.span() => #stream.iter().collect::>().len() } } else { quote_spanned! { f.span() => 0 } }; let main_stream = match (&f.ty, params) { ( _, ParamsField { until: Some(UntilParams(d, u, until)), .. }, ) => quote_spanned! { f.span() => androscalpel_serializer::SerializableUntil::<#d, #u>::size(&#field_ref, #until) }, (Type::Array(_), ParamsField { until: None, .. }) => quote_spanned! { f.span() => #field_ref.iter().map(androscalpel_serializer::Serializable::size).sum::() }, (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() => androscalpel_serializer::Serializable::size(&#field_ref) }, }; quote_spanned! { f.span() => #prefix_stream + #main_stream + #suffix_stream } } /// Return the implementation of the [`androscalpel_serializer::Serializable::size`]. fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream { match *data { Data::Struct(ref data) => match data.fields { Fields::Named(ref fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; get_implem_size_for_field(f, quote! { self.#name }) }); quote! { 0 #(+ #recurse)* } } Fields::Unnamed(ref fields) => { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let index = Index::from(i); get_implem_size_for_field(f, quote! { self.#index }) }); quote! { 0 #(+ #recurse)* } } Fields::Unit => quote!(0), }, Data::Enum(ref data) => { let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect( "Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute", ); let recurse = data.variants.iter().map(|v| { let ident = &v.ident; let params = ParamsVariant::parse(&v.attrs); // let prefix = params.prefix.expect( // "Cannot derive Serializable for variant without the #[prefix(val)] attribute", // ); // let PrefixParamsVariant(val) = prefix; let prefix_quote = match params { ParamsVariant {prefix: Some(PrefixParamsVariant(val)), default_variant: None } => quote_spanned! { v.span() => <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) }, ParamsVariant {prefix: None, default_variant: Some(()) } => quote! { 0 }, // The prefix is in the first field ParamsVariant {prefix: None, default_variant: None } => panic!("Cannot derive Serializable for variant without the #[prefix(val)] or #[default_variant] attribute"), ParamsVariant {prefix: Some(_), default_variant: Some(()) } => panic! ("A variant cannot be the default one and specify a prefix value"), }; let match_ = get_enum_match(v); let body = match v.fields { Fields::Named(ref fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; get_implem_size_for_field(f, quote! { *#name }) }); quote_spanned! { v.span() => //<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )* #prefix_quote #(+#recurse )* } } Fields::Unnamed(ref fields) => { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let ident = format_ident!("field{}", i); get_implem_size_for_field(f, quote! { *#ident }) }); quote_spanned! { v.span() => //<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )* #prefix_quote #(+#recurse )* } } //Fields::Unit => quote! { <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) }, Fields::Unit => quote! { #prefix_quote }, }; quote_spanned! { v.span() => Self::#ident #match_ => { #body }, } }); quote! { match self { #(#recurse)* }} } Data::Union(_) => unimplemented!(), } } /// Return the implementation of the computation of [`androscalpel_serializer::Serializable::serialize`] /// for a specific field `f` accessible using `field_ref`. fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream { let params = ParamsField::parse(&f.attrs); // TODO: Improve error handling let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix { quote_spanned! { f.span() => for byte in #stream { ::serialize(&byte, output)?; } } } else { quote_spanned! { f.span() => } }; let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix { quote_spanned! { f.span() => for byte in #stream { ::serialize(&byte, output)?; } } } else { quote_spanned! { f.span() => } }; let main_stream = match (&f.ty, params) { ( _, ParamsField { until: Some(UntilParams(d, u, until)), .. }, ) => quote_spanned! { f.span() => androscalpel_serializer::SerializableUntil::<#d, #u>::serialize(&#field_ref, output, #until)?; }, (Type::Array(_), ParamsField { until: None, .. }) => quote_spanned! { f.span() => for x in #field_ref { androscalpel_serializer::Serializable::serialize(&x, output)?; } }, (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() => androscalpel_serializer::Serializable::serialize(&#field_ref, output)?; }, }; quote_spanned! { f.span() => #prefix_stream #main_stream #suffix_stream } } /// Return the implementation of the [`androscalpel_serializer::Serializable::serialize`]. fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream { match *data { Data::Struct(ref data) => match data.fields { Fields::Named(ref fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; get_implem_serialize_for_field(f, quote! { self.#name }) }); quote! { #(#recurse)* Ok(()) } } Fields::Unnamed(ref fields) => { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let index = Index::from(i); get_implem_serialize_for_field(f, quote! { self.#index }) }); quote! { #(#recurse)* Ok(()) } } Fields::Unit => quote!(0), }, Data::Enum(ref data) => { let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect( "Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute", ); let recurse = data.variants.iter().map(|v| { let ident = &v.ident; let params = ParamsVariant::parse(&v.attrs); //let prefix = params.prefix.expect( // "Cannot derive Serializable for variant without the #[prefix(val)] attribute", //); //let PrefixParamsVariant(val) = prefix; let prefix_quote = match params { ParamsVariant {prefix: Some(PrefixParamsVariant(val)), default_variant: None } => quote_spanned! { v.span() => <#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; }, ParamsVariant {prefix: None, default_variant: Some(()) } => quote! {}, ParamsVariant {prefix: None, default_variant: None } => panic!("Cannot derive Serializable for variant without the #[prefix(val)] or #[default_variant] attribute"), ParamsVariant {prefix: Some(_), default_variant: Some(()) } => panic! ("A variant cannot be the default one and specify a prefix value"), }; let match_ = get_enum_match(v); let body = match v.fields { Fields::Named(ref fields) => { if params.default_variant.is_some() && fields .named .first() .unwrap() .ident .as_ref() .expect( "Default variant must have a least one field for the prefix" ) != "prefix" { panic!( "The first field of a named field variant must be named `prefix` and of the type of the prefix of the enum" ); } let recurse = fields.named.iter().map(|f| { let name = &f.ident; get_implem_serialize_for_field(f, quote! { *#name }) }); quote_spanned! { v.span() => //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; #prefix_quote #(#recurse)* } } Fields::Unnamed(ref fields) => { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let ident = format_ident!("field{}", i); get_implem_serialize_for_field(f, quote! { *#ident }) }); quote_spanned! { v.span() => //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; #prefix_quote #(#recurse)* } } Fields::Unit => quote! { //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; #prefix_quote }, }; quote_spanned! { v.span() => Self::#ident #match_ => { #body }, } }); quote! { match self { #(#recurse)* } Ok(()) } } Data::Union(_) => unimplemented!(), } } /// Return the implementation of the computation of [`androscalpel_serializer::Serializable::deserialize`] /// for a specific field `f` accessible using `field_ref`. fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream { let params = ParamsField::parse(&f.attrs); let ty = &f.ty; // TODO: Improve error handling let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix { quote_spanned! { f.span() => for byte in #stream { if ::deserialize(input)? != byte { return Err(androscalpel_serializer::Error::DeserializationError( "Prefix do not match #stream".into() )); } } } } else { quote_spanned! { f.span() => } }; let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix { quote_spanned! { f.span() => for byte in #stream { if ::deserialize(input)? != byte { return Err(androscalpel_serializer::Error::DeserializationError( "Suffix do not match #stream".into() )); } } } } else { quote_spanned! { f.span() => } }; match (ty, params) { ( _, ParamsField { until: Some(UntilParams(d, u, until)), .. }, ) => quote_spanned! { f.span() => #field_ref { #prefix_stream let val = <#ty as androscalpel_serializer::SerializableUntil::<#d, #u>>::deserialize(input, #until)?; #suffix_stream val }, }, (Type::Array(arr), ParamsField { until: None, .. }) => { let len = &arr.len; let arr_ty = &arr.elem; quote_spanned! { f.span() => #field_ref { #prefix_stream let mut vec_ = vec![]; for _ in 0..(#len) { vec_.push(<#arr_ty as androscalpel_serializer::Serializable>::deserialize(input)?); } #suffix_stream vec_.try_into().unwrap() }, } } (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() => #field_ref { #prefix_stream let val = <#ty as androscalpel_serializer::Serializable>::deserialize(input)?; #suffix_stream val }, }, } } /// Return the implementation of the [`androscalpel_serializer::Serializable::deserialize`]. fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream { match *data { Data::Struct(ref data) => match data.fields { Fields::Named(ref fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; get_implem_deserialize_for_field(f, quote! { #name: }) }); quote! { Ok( Self { #(#recurse)* } ) } } Fields::Unnamed(ref fields) => { let recurse = fields .unnamed .iter() .map(|f| get_implem_deserialize_for_field(f, quote! {})); quote! { Ok(Self(#(#recurse)*)) } } Fields::Unit => quote!(0), }, Data::Enum(ref data) => { let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect( "Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute", ); let recurse = data.variants.iter().map(|v| { let v_ident = &v.ident; let v_params = ParamsVariant::parse(&v.attrs); // Serialization of default value at the end if v_params.default_variant.is_some() { return quote! {}; } let PrefixParamsVariant(val) = v_params.prefix.expect( "Cannot derive Serializable for variant without the #[prefix(val)] attribute", ); match v.fields { Fields::Named(ref fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; get_implem_deserialize_for_field(f, quote! { #name: }) }); quote_spanned! { v.span() => if #val == prefix { return Ok(Self::#v_ident {#(#recurse)*}); } } } Fields::Unnamed(ref fields) => { let recurse = fields .unnamed .iter() .map(|f| get_implem_deserialize_for_field(f, quote! {})); quote_spanned! { v.span() => if #val == prefix { return Ok(Self::#v_ident (#(#recurse)*)); } } } Fields::Unit => quote_spanned! { v.span() => if #val == prefix { return Ok(Self::#v_ident); } }, } }); let mut default_variant = None; for v in &data.variants { let v_params = ParamsVariant::parse(&v.attrs); if v_params.default_variant.is_some() && v_params.prefix.is_some() { panic!("A variant cannot be both the default variant and check a prefix value"); } if v_params.default_variant.is_some() && default_variant.is_some() { panic!("An enum can only have one variant marked as `default_variant`"); } if v_params.default_variant.is_some() { default_variant = Some(v); } } let default_quote = match default_variant { None => quote! { Err(androscalpel_serializer::Error::SerializationError(format!( "Found prefix {:?} that did not match any variant.", prefix ))) }, Some(v) => { let v_ident = &v.ident; match v.fields { Fields::Named(ref fields) => { // Skip 1 because the first field is the prefix // TODO: check first field name is `prefix`? let recurse = fields.named.iter().skip(1).map(|f| { let name = &f.ident; get_implem_deserialize_for_field(f, quote! { #name: }) }); quote_spanned! { v.span() => return Ok(Self::#v_ident {prefix, #(#recurse)*}); } } Fields::Unnamed(ref fields) => { let recurse = fields .unnamed .iter() .skip(1) .map(|f| get_implem_deserialize_for_field(f, quote! {})); quote_spanned! { v.span() => return Ok(Self::#v_ident (prefix, #(#recurse)*)); } } Fields::Unit => quote_spanned! { v.span() => return Ok(Self::#v_ident); }, } } }; quote! { let prefix = <#prefix_ty as androscalpel_serializer::Serializable>::deserialize(input)?; #(#recurse)* //Err(androscalpel_serializer::Error::SerializationError(format!( // "Found prefix {:?} that did not match any variant.", prefix //))) #default_quote } } Data::Union(_) => unimplemented!(), } }