add default variant

This commit is contained in:
Jean-Marie Mineau 2023-08-28 15:02:48 +02:00
parent ae1c3e20ac
commit 7557022900
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
2 changed files with 240 additions and 21 deletions

View file

@ -880,4 +880,85 @@ mod test {
vec![0x42, 0, 1, 2, 3, 0x66, 0x66] vec![0x42, 0, 1, 2, 3, 0x66, 0x66]
); );
} }
#[derive(Serializable, PartialEq, Eq, Debug)]
#[prefix_type(u8)]
enum TestEnumUnnamedDef {
#[prefix(0)]
Zero,
#[prefix(1)]
One(u32),
#[prefix(2)]
Two { a: u16, b: u32 },
#[default_variant]
Dft(u8, u32),
}
#[test]
fn size_enum_unnamed_def() {
assert_eq!(TestEnumUnnamedDef::Zero.size(), 1);
assert_eq!(TestEnumUnnamedDef::One(42).size(), 5);
assert_eq!(TestEnumUnnamedDef::Two { a: 1, b: 2 }.size(), 7);
assert_eq!(TestEnumUnnamedDef::Dft(42, 0x01234567).size(), 5);
}
#[test]
fn serialize_enum_unnamed_def() {
assert_eq!(
TestEnumUnnamedDef::Zero.serialize_to_vec().unwrap(),
vec![0u8]
);
assert_eq!(
TestEnumUnnamedDef::One(42).serialize_to_vec().unwrap(),
vec![1u8, 0u8, 0u8, 0u8, 42u8]
);
assert_eq!(
TestEnumUnnamedDef::Two { a: 1, b: 2 }
.serialize_to_vec()
.unwrap(),
vec![2u8, 0u8, 1u8, 0u8, 0u8, 0u8, 2u8]
);
assert_eq!(
TestEnumUnnamedDef::Dft(42, 0x01234567)
.serialize_to_vec()
.unwrap(),
vec![42u8, 0x01u8, 0x23u8, 0x45, 0x67],
);
}
#[test]
fn deserialize_enum_unnamed_def() {
assert_eq!(
TestEnumUnnamedDef::deserialize_from_slice(&[0u8]).unwrap(),
TestEnumUnnamedDef::Zero
);
assert_eq!(
TestEnumUnnamedDef::deserialize_from_slice(&[1u8, 0u8, 0u8, 0u8, 42u8]).unwrap(),
TestEnumUnnamedDef::One(42)
);
assert_eq!(
TestEnumUnnamedDef::deserialize_from_slice(&[2u8, 0u8, 1u8, 0u8, 0u8, 0u8, 2u8])
.unwrap(),
TestEnumUnnamedDef::Two { a: 1, b: 2 }
);
assert_eq!(
TestEnumUnnamedDef::deserialize_from_slice(&[42u8, 0x01u8, 0x23u8, 0x45, 0x67])
.unwrap(),
TestEnumUnnamedDef::Dft(42, 0x01234567)
);
}
#[derive(Serializable, PartialEq, Eq, Debug)]
#[prefix_type(u8)]
enum TestEnumNamedDef {
#[default_variant]
Dft { prefix: u8 },
}
// Must panic: TODO: test that, somehow
// #[derive(Serializable, PartialEq, Eq, Debug)]
// #[prefix_type(u8)]
// enum TestInvalidEnumNamedDef {
// #[default_variant]
// Dft { plop: u32, prefix: u8 },
// }
} }

View file

@ -17,7 +17,7 @@ use syn::{
/// the type of the repeated data `D`, the type of the symbole at the end of the list `U`, and /// 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`. /// the specific value of the symbole at the end of the list `end`.
/// ///
/// For example, we can deserialize a Vec<u8> until we find 0x0000: /// For example, we can deserialize a `Vec<u8>` until we find `0x0000`:
/// ///
/// `[0, 1, 2, 3, 0, 0]` would be serialized as `vec![0, 1, 2, 3]` /// `[0, 1, 2, 3, 0, 0]` would be serialized as `vec![0, 1, 2, 3]`
/// ///
@ -42,9 +42,57 @@ use syn::{
/// ); /// );
/// ``` /// ```
/// ///
/// ## Enum /// ## Enums
/// ///
/// TODO /// 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, 0x01u8, 0x23u8, 0x45, 0x67]).unwrap(),
/// Example::One(0x01234567)
/// );
/// assert_eq!(
/// Example::deserialize_from_slice(&[2u8, 0xFEu8, 0xDCu8, 0x01u8, 0x23u8, 0x45, 0x67]).unwrap(),
/// Example::Two { a: 0xFEDC, b: 0x01234567 }
/// );
/// assert_eq!(
/// Example::deserialize_from_slice(&[42u8, 0x01u8, 0x23u8, 0x45, 0x67]).unwrap(),
/// Example::Dft(42, 0x01234567)
/// );
/// ```
/// ///
/// ## Prefix and suffix /// ## Prefix and suffix
/// ///
@ -68,7 +116,7 @@ use syn::{
/// ``` /// ```
/// ///
/// ///
#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type, suffix))] #[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type, suffix, default_variant))]
pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let name = input.ident; let name = input.ident;
@ -165,6 +213,7 @@ struct ParamsField {
#[derive(Default)] #[derive(Default)]
struct ParamsVariant { struct ParamsVariant {
pub prefix: Option<PrefixParamsVariant>, pub prefix: Option<PrefixParamsVariant>,
pub default_variant: Option<()>,
} }
impl ParamsStruct { impl ParamsStruct {
@ -224,6 +273,9 @@ impl ParamsVariant {
Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => { Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => {
panic!("Variant cannot take the attribut 'prefix_type'") panic!("Variant cannot take the attribut 'prefix_type'")
} }
Meta::Path(path) if path.is_ident("default_variant") => {
params.default_variant = Some(());
}
_ => (), _ => (),
} }
} }
@ -327,10 +379,16 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
let recurse = data.variants.iter().map(|v| { let recurse = data.variants.iter().map(|v| {
let ident = &v.ident; let ident = &v.ident;
let params = ParamsVariant::parse(&v.attrs); let params = ParamsVariant::parse(&v.attrs);
let prefix = params.prefix.expect( // let prefix = params.prefix.expect(
"Cannot derive Serializable for variant without the #[prefix(val)] attribute", // "Cannot derive Serializable for variant without the #[prefix(val)] attribute",
); // );
let PrefixParamsVariant(val) = prefix; // 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 match_ = get_enum_match(v);
let body = match v.fields { let body = match v.fields {
Fields::Named(ref fields) => { Fields::Named(ref fields) => {
@ -339,7 +397,8 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
get_implem_size_for_field(f, quote! { *#name }) get_implem_size_for_field(f, quote! { *#name })
}); });
quote_spanned! { v.span() => quote_spanned! { v.span() =>
<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )* //<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )*
#prefix_quote #(+#recurse )*
} }
} }
Fields::Unnamed(ref fields) => { Fields::Unnamed(ref fields) => {
@ -348,10 +407,12 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
get_implem_size_for_field(f, quote! { *#ident }) get_implem_size_for_field(f, quote! { *#ident })
}); });
quote_spanned! { v.span() => quote_spanned! { v.span() =>
<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )* //<#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_ty as androscalpel_serializer::Serializable>::size(&#val) },
Fields::Unit => quote! { #prefix_quote },
}; };
quote_spanned! { v.span() => quote_spanned! { v.span() =>
@ -447,19 +508,40 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
let recurse = data.variants.iter().map(|v| { let recurse = data.variants.iter().map(|v| {
let ident = &v.ident; let ident = &v.ident;
let params = ParamsVariant::parse(&v.attrs); let params = ParamsVariant::parse(&v.attrs);
let prefix = params.prefix.expect( //let prefix = params.prefix.expect(
"Cannot derive Serializable for variant without the #[prefix(val)] attribute", // "Cannot derive Serializable for variant without the #[prefix(val)] attribute",
); //);
let PrefixParamsVariant(val) = prefix; //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 match_ = get_enum_match(v);
let body = match v.fields { let body = match v.fields {
Fields::Named(ref 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 recurse = fields.named.iter().map(|f| {
let name = &f.ident; let name = &f.ident;
get_implem_serialize_for_field(f, quote! { *#name }) get_implem_serialize_for_field(f, quote! { *#name })
}); });
quote_spanned! { v.span() => quote_spanned! { v.span() =>
<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
#prefix_quote
#(#recurse)* #(#recurse)*
} }
} }
@ -469,12 +551,14 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
get_implem_serialize_for_field(f, quote! { *#ident }) get_implem_serialize_for_field(f, quote! { *#ident })
}); });
quote_spanned! { v.span() => quote_spanned! { v.span() =>
<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
#prefix_quote
#(#recurse)* #(#recurse)*
} }
} }
Fields::Unit => quote! { Fields::Unit => quote! {
<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
#prefix_quote
}, },
}; };
@ -601,6 +685,10 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
let recurse = data.variants.iter().map(|v| { let recurse = data.variants.iter().map(|v| {
let v_ident = &v.ident; let v_ident = &v.ident;
let v_params = ParamsVariant::parse(&v.attrs); 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( let PrefixParamsVariant(val) = v_params.prefix.expect(
"Cannot derive Serializable for variant without the #[prefix(val)] attribute", "Cannot derive Serializable for variant without the #[prefix(val)] attribute",
); );
@ -634,13 +722,63 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
}, },
} }
}); });
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! { quote! {
let prefix = <#prefix_ty as androscalpel_serializer::Serializable>::deserialize(input)?; let prefix = <#prefix_ty as androscalpel_serializer::Serializable>::deserialize(input)?;
#(#recurse)* #(#recurse)*
Err(androscalpel_serializer::Error::SerializationError(format!( //Err(androscalpel_serializer::Error::SerializationError(format!(
"Found prefix {:?} that did not match any variant.", prefix // "Found prefix {:?} that did not match any variant.", prefix
))) //)))
#default_quote
} }
} }
Data::Union(_) => unimplemented!(), Data::Union(_) => unimplemented!(),