add default variant
This commit is contained in:
parent
ae1c3e20ac
commit
7557022900
2 changed files with 240 additions and 21 deletions
|
|
@ -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 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]`
|
||||
///
|
||||
|
|
@ -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
|
||||
///
|
||||
|
|
@ -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 {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = input.ident;
|
||||
|
|
@ -165,6 +213,7 @@ struct ParamsField {
|
|||
#[derive(Default)]
|
||||
struct ParamsVariant {
|
||||
pub prefix: Option<PrefixParamsVariant>,
|
||||
pub default_variant: Option<()>,
|
||||
}
|
||||
|
||||
impl ParamsStruct {
|
||||
|
|
@ -224,6 +273,9 @@ impl ParamsVariant {
|
|||
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(());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -327,10 +379,16 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
|
|||
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 = 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) => {
|
||||
|
|
@ -339,7 +397,8 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
|
|||
get_implem_size_for_field(f, quote! { *#name })
|
||||
});
|
||||
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) => {
|
||||
|
|
@ -348,10 +407,12 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
|
|||
get_implem_size_for_field(f, quote! { *#ident })
|
||||
});
|
||||
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() =>
|
||||
|
|
@ -447,19 +508,40 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
|
|||
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 = 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_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
|
||||
#prefix_quote
|
||||
#(#recurse)*
|
||||
}
|
||||
}
|
||||
|
|
@ -469,12 +551,14 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
|
|||
get_implem_serialize_for_field(f, quote! { *#ident })
|
||||
});
|
||||
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)*
|
||||
}
|
||||
}
|
||||
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 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",
|
||||
);
|
||||
|
|
@ -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! {
|
||||
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
|
||||
)))
|
||||
//Err(androscalpel_serializer::Error::SerializationError(format!(
|
||||
// "Found prefix {:?} that did not match any variant.", prefix
|
||||
//)))
|
||||
#default_quote
|
||||
}
|
||||
}
|
||||
Data::Union(_) => unimplemented!(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue