add default variant
This commit is contained in:
parent
ae1c3e20ac
commit
7557022900
2 changed files with 240 additions and 21 deletions
|
|
@ -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 },
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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!(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue