add prefix / suffix to field
This commit is contained in:
parent
33e770e04a
commit
5dd96fb173
4 changed files with 176 additions and 22 deletions
3
androscalpel_serializer/src/constant.rs
Normal file
3
androscalpel_serializer/src/constant.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
//! Constants definition
|
||||||
|
|
||||||
|
pub use androscalpel_serializer_derive::*;
|
||||||
|
|
@ -5,7 +5,7 @@ use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
pub use androscalpel_serializer_derive::*;
|
pub use androscalpel_serializer_derive::*;
|
||||||
|
|
||||||
mod leb;
|
pub mod leb;
|
||||||
pub use leb::*;
|
pub use leb::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
|
@ -849,4 +849,29 @@ mod test {
|
||||||
);
|
);
|
||||||
assert!(TestEnum::deserialize_from_slice(&[255u8]).is_err());
|
assert!(TestEnum::deserialize_from_slice(&[255u8]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serializable, PartialEq, Debug)]
|
||||||
|
struct TestPrefixSuffix {
|
||||||
|
#[prefix([0x42])]
|
||||||
|
#[suffix([0x66, 0x66])]
|
||||||
|
a: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_prefix_suffix() {
|
||||||
|
assert_eq!(
|
||||||
|
TestPrefixSuffix::deserialize_from_slice(&[0x42, 0, 1, 2, 3, 0x66, 0x66]).unwrap(),
|
||||||
|
TestPrefixSuffix { a: 0x00010203 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_prefix_suffix() {
|
||||||
|
assert_eq!(
|
||||||
|
TestPrefixSuffix { a: 0x00010203 }
|
||||||
|
.serialize_to_vec()
|
||||||
|
.unwrap(),
|
||||||
|
vec![0x42, 0, 1, 2, 3, 0x66, 0x66]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod constant;
|
||||||
pub mod core;
|
pub mod core;
|
||||||
|
|
||||||
pub use crate::core::*;
|
pub use crate::core::*;
|
||||||
|
pub use constant::*;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,29 @@ use syn::{
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type))]
|
/// ## Prefix and suffix
|
||||||
|
///
|
||||||
|
/// To define a constant prefix before a serialised field or suffixe after, use `#[prefix(<prefix>)]`
|
||||||
|
/// and `#[suffix(<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, 0, 1, 2, 3, 0x66, 0x66]).unwrap(),
|
||||||
|
/// Example { a: 0x00010203 }
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
///
|
||||||
|
#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type, suffix))]
|
||||||
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;
|
||||||
|
|
@ -83,9 +105,9 @@ impl Parse for UntilParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parsed Parameters for the `#[prefix(val)]` attribute.
|
/// Parsed Parameters for the `#[prefix(val)]` attribute of an enum variant.
|
||||||
struct PrefixParams(TokenStream);
|
struct PrefixParamsVariant(TokenStream);
|
||||||
impl Parse for PrefixParams {
|
impl Parse for PrefixParamsVariant {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let value = input.parse()?;
|
let value = input.parse()?;
|
||||||
Ok(Self(value))
|
Ok(Self(value))
|
||||||
|
|
@ -107,16 +129,36 @@ struct ParamsStruct {
|
||||||
pub prefix_type: Option<PrefixTypeParams>,
|
pub prefix_type: Option<PrefixTypeParams>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parsed Parameters for the `#[prefix(val)]` attribute of an struct field.
|
||||||
|
struct PrefixParamsField(TokenStream);
|
||||||
|
impl Parse for PrefixParamsField {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
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<Self> {
|
||||||
|
let value = input.parse()?;
|
||||||
|
Ok(Self(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// All the attributes parameters for a field
|
/// All the attributes parameters for a field
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ParamsField {
|
struct ParamsField {
|
||||||
pub until: Option<UntilParams>,
|
pub until: Option<UntilParams>,
|
||||||
|
pub prefix: Option<PrefixParamsField>,
|
||||||
|
pub suffix: Option<SuffixParamsField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the attributes parameters for a variant
|
/// All the attributes parameters for a variant
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ParamsVariant {
|
struct ParamsVariant {
|
||||||
pub prefix: Option<PrefixParams>,
|
pub prefix: Option<PrefixParamsVariant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParamsStruct {
|
impl ParamsStruct {
|
||||||
|
|
@ -147,8 +189,11 @@ impl ParamsField {
|
||||||
Meta::List(MetaList { path, tokens, .. }) if path.is_ident("until") => {
|
Meta::List(MetaList { path, tokens, .. }) if path.is_ident("until") => {
|
||||||
params.until = Some(syn::parse2(tokens.clone()).unwrap())
|
params.until = Some(syn::parse2(tokens.clone()).unwrap())
|
||||||
}
|
}
|
||||||
Meta::List(MetaList { path, .. }) if path.is_ident("prefix") => {
|
Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix") => {
|
||||||
panic!("Fields cannot take the attribut '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") => {
|
Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => {
|
||||||
panic!("Fields cannot take the attribut 'prefix_type'")
|
panic!("Fields cannot take the attribut 'prefix_type'")
|
||||||
|
|
@ -211,22 +256,38 @@ fn get_enum_match(variant: &Variant) -> TokenStream {
|
||||||
/// for a specific field `f` accessible using `field_ref`.
|
/// for a specific field `f` accessible using `field_ref`.
|
||||||
fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
|
fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
|
||||||
let params = ParamsField::parse(&f.attrs);
|
let params = ParamsField::parse(&f.attrs);
|
||||||
match (&f.ty, params) {
|
let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix {
|
||||||
|
quote_spanned! { f.span() =>
|
||||||
|
#stream.iter().collect::<Vec<_>>().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::<Vec<_>>().len()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! { f.span() => 0 }
|
||||||
|
};
|
||||||
|
let main_stream = match (&f.ty, params) {
|
||||||
(
|
(
|
||||||
_,
|
_,
|
||||||
ParamsField {
|
ParamsField {
|
||||||
until: Some(UntilParams(d, u, until)),
|
until: Some(UntilParams(d, u, until)),
|
||||||
|
..
|
||||||
},
|
},
|
||||||
) => quote_spanned! { f.span() =>
|
) => quote_spanned! { f.span() =>
|
||||||
androscalpel_serializer::SerializableUntil::<#d, #u>::size(&#field_ref, #until)
|
androscalpel_serializer::SerializableUntil::<#d, #u>::size(&#field_ref, #until)
|
||||||
},
|
},
|
||||||
(Type::Array(_), ParamsField { until: None }) => quote_spanned! { f.span() =>
|
(Type::Array(_), ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
|
||||||
#field_ref.iter().map(androscalpel_serializer::Serializable::size).sum::<usize>()
|
#field_ref.iter().map(androscalpel_serializer::Serializable::size).sum::<usize>()
|
||||||
},
|
},
|
||||||
(_, ParamsField { until: None }) => quote_spanned! { f.span() =>
|
(_, ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
|
||||||
androscalpel_serializer::Serializable::size(&#field_ref)
|
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`].
|
/// Return the implementation of the [`androscalpel_serializer::Serializable::size`].
|
||||||
|
|
@ -263,7 +324,7 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
|
||||||
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 PrefixParams(val) = prefix;
|
let PrefixParamsVariant(val) = prefix;
|
||||||
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) => {
|
||||||
|
|
@ -304,23 +365,47 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
|
||||||
fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
|
fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
|
||||||
let params = ParamsField::parse(&f.attrs);
|
let params = ParamsField::parse(&f.attrs);
|
||||||
// TODO: Improve error handling
|
// TODO: Improve error handling
|
||||||
match (&f.ty, params) {
|
let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix {
|
||||||
|
quote_spanned! { f.span() =>
|
||||||
|
for byte in #stream {
|
||||||
|
<u8 as androscalpel_serializer::Serializable>::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 {
|
||||||
|
<u8 as androscalpel_serializer::Serializable>::serialize(&byte, output)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! { f.span() => }
|
||||||
|
};
|
||||||
|
let main_stream = match (&f.ty, params) {
|
||||||
(
|
(
|
||||||
_,
|
_,
|
||||||
ParamsField {
|
ParamsField {
|
||||||
until: Some(UntilParams(d, u, until)),
|
until: Some(UntilParams(d, u, until)),
|
||||||
|
..
|
||||||
},
|
},
|
||||||
) => quote_spanned! { f.span() =>
|
) => quote_spanned! { f.span() =>
|
||||||
androscalpel_serializer::SerializableUntil::<#d, #u>::serialize(&#field_ref, output, #until)?;
|
androscalpel_serializer::SerializableUntil::<#d, #u>::serialize(&#field_ref, output, #until)?;
|
||||||
},
|
},
|
||||||
(Type::Array(_), ParamsField { until: None }) => quote_spanned! { f.span() =>
|
(Type::Array(_), ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
|
||||||
for x in #field_ref {
|
for x in #field_ref {
|
||||||
androscalpel_serializer::Serializable::serialize(&x, output)?;
|
androscalpel_serializer::Serializable::serialize(&x, output)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(_, ParamsField { until: None }) => quote_spanned! { f.span() =>
|
(_, ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
|
||||||
androscalpel_serializer::Serializable::serialize(&#field_ref, output)?;
|
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`].
|
/// Return the implementation of the [`androscalpel_serializer::Serializable::serialize`].
|
||||||
|
|
@ -359,7 +444,7 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
|
||||||
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 PrefixParams(val) = prefix;
|
let PrefixParamsVariant(val) = prefix;
|
||||||
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) => {
|
||||||
|
|
@ -408,30 +493,69 @@ fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenS
|
||||||
let params = ParamsField::parse(&f.attrs);
|
let params = ParamsField::parse(&f.attrs);
|
||||||
let ty = &f.ty;
|
let ty = &f.ty;
|
||||||
// TODO: Improve error handling
|
// TODO: Improve error handling
|
||||||
|
let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix {
|
||||||
|
quote_spanned! { f.span() =>
|
||||||
|
for byte in #stream {
|
||||||
|
if <u8 as androscalpel_serializer::Serializable>::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 <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte {
|
||||||
|
return Err(androscalpel_serializer::Error::DeserializationError(
|
||||||
|
"Suffix do not match #stream".into()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! { f.span() => }
|
||||||
|
};
|
||||||
match (ty, params) {
|
match (ty, params) {
|
||||||
(
|
(
|
||||||
_,
|
_,
|
||||||
ParamsField {
|
ParamsField {
|
||||||
until: Some(UntilParams(d, u, until)),
|
until: Some(UntilParams(d, u, until)),
|
||||||
|
..
|
||||||
},
|
},
|
||||||
) => quote_spanned! { f.span() =>
|
) => quote_spanned! { f.span() =>
|
||||||
#field_ref <#ty as androscalpel_serializer::SerializableUntil::<#d, #u>>::deserialize(input, #until)?,
|
#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 }) => {
|
},
|
||||||
|
(Type::Array(arr), ParamsField { until: None, .. }) => {
|
||||||
let len = &arr.len;
|
let len = &arr.len;
|
||||||
let arr_ty = &arr.elem;
|
let arr_ty = &arr.elem;
|
||||||
quote_spanned! { f.span() =>
|
quote_spanned! { f.span() =>
|
||||||
#field_ref {
|
#field_ref {
|
||||||
|
#prefix_stream
|
||||||
let mut vec_ = vec![];
|
let mut vec_ = vec![];
|
||||||
for _ in 0..(#len) {
|
for _ in 0..(#len) {
|
||||||
vec_.push(<#arr_ty as androscalpel_serializer::Serializable>::deserialize(input)?);
|
vec_.push(<#arr_ty as androscalpel_serializer::Serializable>::deserialize(input)?);
|
||||||
}
|
}
|
||||||
|
#suffix_stream
|
||||||
vec_.try_into().unwrap()
|
vec_.try_into().unwrap()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, ParamsField { until: None }) => quote_spanned! { f.span() =>
|
(_, ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
|
||||||
#field_ref <#ty as androscalpel_serializer::Serializable>::deserialize(input)?,
|
#field_ref {
|
||||||
|
#prefix_stream
|
||||||
|
let val = <#ty as androscalpel_serializer::Serializable>::deserialize(input)?;
|
||||||
|
#suffix_stream
|
||||||
|
val
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -471,7 +595,7 @@ 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);
|
||||||
let PrefixParams(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",
|
||||||
);
|
);
|
||||||
match v.fields {
|
match v.fields {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue