add better errors

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2026-01-12 23:16:40 +01:00
parent 04c7898aa2
commit 4b1cc379a4
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2

View file

@ -3,8 +3,8 @@ use quote::{format_ident, quote, quote_spanned};
use syn::parse::{Parse, ParseStream}; use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::{ use syn::{
Attribute, Data, DeriveInput, Field, Fields, Ident, Index, Meta, MetaList, Token, Type, parse_macro_input, Attribute, Data, DeriveInput, Field, Fields, Ident, Index, Meta, MetaList,
Variant, parse_macro_input, Token, Type, Variant,
}; };
/// Derive the type Serializable. /// Derive the type Serializable.
@ -124,9 +124,9 @@ pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenS
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let name = input.ident; let name = input.ident;
let params = ParamsStruct::parse(&input.attrs); let params = ParamsStruct::parse(&input.attrs);
let implem_serialize = get_implem_serialize(&input.data, &params); let implem_serialize = get_implem_serialize(&input.data, &params, &name);
let implem_deserialize = get_implem_deserialize(&input.data, &params); let implem_deserialize = get_implem_deserialize(&input.data, &params, &name);
let implem_size = get_implem_size(&input.data, &params); let implem_size = get_implem_size(&input.data, &params, &name);
let expanded = quote! { let expanded = quote! {
impl androscalpel_serializer::Serializable for #name { impl androscalpel_serializer::Serializable for #name {
#[allow(clippy::single_element_loop, clippy::let_and_return)] #[allow(clippy::single_element_loop, clippy::let_and_return)]
@ -316,7 +316,11 @@ fn get_enum_match(variant: &Variant) -> TokenStream {
/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::size`] /// Return the implementation of the computation of [`androscalpel_serializer::Serializable::size`]
/// 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,
_struct_name: &Ident,
) -> TokenStream {
let params = ParamsField::parse(&f.attrs); let params = ParamsField::parse(&f.attrs);
let prefix_stream = match params.prefix { let prefix_stream = match params.prefix {
Some(PrefixParamsField(ref stream)) => { Some(PrefixParamsField(ref stream)) => {
@ -359,13 +363,13 @@ fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
} }
/// Return the implementation of the [`androscalpel_serializer::Serializable::size`]. /// Return the implementation of the [`androscalpel_serializer::Serializable::size`].
fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream { fn get_implem_size(data: &Data, params: &ParamsStruct, struct_name: &Ident) -> TokenStream {
match *data { match *data {
Data::Struct(ref data) => match data.fields { Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => { Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| { let recurse = fields.named.iter().map(|f| {
let name = &f.ident; let name = &f.ident;
get_implem_size_for_field(f, quote! { self.#name }) get_implem_size_for_field(f, quote! { self.#name }, struct_name)
}); });
quote! { quote! {
0 #(+ #recurse)* 0 #(+ #recurse)*
@ -374,7 +378,7 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Unnamed(ref fields) => { Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
let index = Index::from(i); let index = Index::from(i);
get_implem_size_for_field(f, quote! { self.#index }) get_implem_size_for_field(f, quote! { self.#index }, struct_name)
}); });
quote! { quote! {
0 #(+ #recurse)* 0 #(+ #recurse)*
@ -404,7 +408,7 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Named(ref fields) => { Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| { let recurse = fields.named.iter().map(|f| {
let name = &f.ident; let name = &f.ident;
get_implem_size_for_field(f, quote! { *#name }) get_implem_size_for_field(f, quote! { *#name }, struct_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 )*
@ -414,7 +418,7 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Unnamed(ref fields) => { Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
let ident = format_ident!("field{}", i); let ident = format_ident!("field{}", i);
get_implem_size_for_field(f, quote! { *#ident }) get_implem_size_for_field(f, quote! { *#ident }, struct_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 )*
@ -439,14 +443,22 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::serialize`] /// Return the implementation of the computation of [`androscalpel_serializer::Serializable::serialize`]
/// for a specific field `f` accessible using `field_ref`. /// for a specific field `f` accessible using `field_ref`.
fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream { fn get_implem_serialize_for_field(
f: &Field,
field_ref: TokenStream,
struct_name: &Ident,
) -> TokenStream {
let params = ParamsField::parse(&f.attrs); let params = ParamsField::parse(&f.attrs);
// TODO: Improve error handling let ident = &f.ident;
let prefix_stream = match params.prefix { let prefix_stream = match params.prefix {
Some(PrefixParamsField(ref stream)) => { Some(PrefixParamsField(ref stream)) => {
quote_spanned! { f.span() => quote_spanned! { f.span() =>
for byte in #stream { for byte in #stream {
<u8 as androscalpel_serializer::Serializable>::serialize(&byte, output)?; <u8 as androscalpel_serializer::Serializable>::serialize(&byte, output).map_err(
|err| androscalpel_serializer::Error::SerializationError(
format!("Failed to serialize '{}.{}' prefix due to: {}", stringify!(#struct_name), stringify!(#ident), err)
)
)?;
} }
} }
} }
@ -458,7 +470,11 @@ fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStr
Some(SuffixParamsField(ref stream)) => { Some(SuffixParamsField(ref stream)) => {
quote_spanned! { f.span() => quote_spanned! { f.span() =>
for byte in #stream { for byte in #stream {
<u8 as androscalpel_serializer::Serializable>::serialize(&byte, output)?; <u8 as androscalpel_serializer::Serializable>::serialize(&byte, output).map_err(
|err| androscalpel_serializer::Error::SerializationError(
format!("Failed to serialize '{}.{}' suffix due to: {}", stringify!(#struct_name), stringify!(#ident), err)
)
)?;
} }
} }
} }
@ -474,15 +490,27 @@ fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStr
.. ..
}, },
) => 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).map_err(
|err| androscalpel_serializer::Error::SerializationError(
format!("Failed to serialize '{}.{}' due to: {}", stringify!(#struct_name), stringify!(#ident), err)
)
)?;
}, },
(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).map_err(
|err| androscalpel_serializer::Error::SerializationError(
format!("Failed to serialize '{}.{}' due to: {}", stringify!(#struct_name), stringify!(#ident), err)
)
)?;
} }
}, },
(_, 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).map_err(
|err| androscalpel_serializer::Error::SerializationError(
format!("Failed to serialize '{}.{}' due to: {}", stringify!(#struct_name), stringify!(#ident), err)
)
)?;
}, },
}; };
quote_spanned! { f.span() => quote_spanned! { f.span() =>
@ -492,13 +520,13 @@ fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStr
} }
} }
/// Return the implementation of the [`androscalpel_serializer::Serializable::serialize`]. /// Return the implementation of the [`androscalpel_serializer::Serializable::serialize`].
fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream { fn get_implem_serialize(data: &Data, params: &ParamsStruct, struct_name: &Ident) -> TokenStream {
match *data { match *data {
Data::Struct(ref data) => match data.fields { Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => { Fields::Named(ref fields) => {
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! { self.#name }) get_implem_serialize_for_field(f, quote! { self.#name }, struct_name)
}); });
quote! { quote! {
#(#recurse)* #(#recurse)*
@ -508,7 +536,7 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Unnamed(ref fields) => { Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
let index = Index::from(i); let index = Index::from(i);
get_implem_serialize_for_field(f, quote! { self.#index }) get_implem_serialize_for_field(f, quote! { self.#index }, struct_name)
}); });
quote! { quote! {
#(#recurse)* #(#recurse)*
@ -552,7 +580,7 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
} }
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 }, struct_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)?;
@ -563,7 +591,7 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Unnamed(ref fields) => { Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
let ident = format_ident!("field{}", i); let ident = format_ident!("field{}", i);
get_implem_serialize_for_field(f, quote! { *#ident }) get_implem_serialize_for_field(f, quote! { *#ident }, struct_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)?;
@ -594,17 +622,21 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::deserialize`] /// Return the implementation of the computation of [`androscalpel_serializer::Serializable::deserialize`]
/// for a specific field `f` accessible using `field_ref`. /// for a specific field `f` accessible using `field_ref`.
fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream { fn get_implem_deserialize_for_field(
f: &Field,
field_ref: TokenStream,
struct_name: &Ident,
) -> TokenStream {
let params = ParamsField::parse(&f.attrs); let params = ParamsField::parse(&f.attrs);
let ty = &f.ty; let ty = &f.ty;
// TODO: Improve error handling let ident = &f.ident;
let prefix_stream = match params.prefix { let prefix_stream = match params.prefix {
Some(PrefixParamsField(ref stream)) => { Some(PrefixParamsField(ref stream)) => {
quote_spanned! { f.span() => quote_spanned! { f.span() =>
for byte in #stream { for byte in #stream {
if <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte { if <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte {
return Err(androscalpel_serializer::Error::DeserializationError( return Err(androscalpel_serializer::Error::DeserializationError(
"Prefix do not match #stream".into() format!("Prefix for '{}.{}' do not match {}", stringify!(#struct_name), stringify!(#ident), stringify!(#stream))
)); ));
} }
} }
@ -620,7 +652,7 @@ fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenS
for byte in #stream { for byte in #stream {
if <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte { if <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte {
return Err(androscalpel_serializer::Error::DeserializationError( return Err(androscalpel_serializer::Error::DeserializationError(
"Suffix do not match #stream".into() format!("Suffix for '{}.{}' do not match {}", stringify!(#struct_name), stringify!(#ident), stringify!(stream))
)); ));
} }
} }
@ -640,7 +672,11 @@ fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenS
) => quote_spanned! { f.span() => ) => quote_spanned! { f.span() =>
#field_ref { #field_ref {
#prefix_stream #prefix_stream
let val = <#ty as androscalpel_serializer::SerializableUntil::<#d, #u>>::deserialize(input, #until)?; let val = <#ty as androscalpel_serializer::SerializableUntil::<#d, #u>>::deserialize(input, #until).map_err(
|err| androscalpel_serializer::Error::DeserializationError(
format!("Failed to deserialize '{}.{}' due to: {}", stringify!(#struct_name), stringify!(#ident), err)
)
)?;
#suffix_stream #suffix_stream
val val
}, },
@ -653,7 +689,13 @@ fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenS
#prefix_stream #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).map_err(
|err| androscalpel_serializer::Error::DeserializationError(
format!("Failed to deserialize '{}.{}' due to: {}", stringify!(#struct_name), stringify!(#ident), err)
)
)?
);
} }
#suffix_stream #suffix_stream
vec_.try_into().unwrap() vec_.try_into().unwrap()
@ -672,13 +714,13 @@ fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenS
} }
/// Return the implementation of the [`androscalpel_serializer::Serializable::deserialize`]. /// Return the implementation of the [`androscalpel_serializer::Serializable::deserialize`].
fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream { fn get_implem_deserialize(data: &Data, params: &ParamsStruct, struct_name: &Ident) -> TokenStream {
match *data { match *data {
Data::Struct(ref data) => match data.fields { Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => { Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| { let recurse = fields.named.iter().map(|f| {
let name = &f.ident; let name = &f.ident;
get_implem_deserialize_for_field(f, quote! { #name: }) get_implem_deserialize_for_field(f, quote! { #name: }, struct_name)
}); });
quote! { quote! {
Ok( Ok(
@ -692,7 +734,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
let recurse = fields let recurse = fields
.unnamed .unnamed
.iter() .iter()
.map(|f| get_implem_deserialize_for_field(f, quote! {})); .map(|f| get_implem_deserialize_for_field(f, quote! {}, struct_name));
quote! { quote! {
Ok(Self(#(#recurse)*)) Ok(Self(#(#recurse)*))
} }
@ -717,7 +759,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Named(ref fields) => { Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| { let recurse = fields.named.iter().map(|f| {
let name = &f.ident; let name = &f.ident;
get_implem_deserialize_for_field(f, quote! { #name: }) get_implem_deserialize_for_field(f, quote! { #name: }, struct_name)
}); });
quote_spanned! { v.span() => quote_spanned! { v.span() =>
if #val == prefix { if #val == prefix {
@ -729,7 +771,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
let recurse = fields let recurse = fields
.unnamed .unnamed
.iter() .iter()
.map(|f| get_implem_deserialize_for_field(f, quote! {})); .map(|f| get_implem_deserialize_for_field(f, quote! {}, struct_name));
quote_spanned! { v.span() => quote_spanned! { v.span() =>
if #val == prefix { if #val == prefix {
return Ok(Self::#v_ident (#(#recurse)*)); return Ok(Self::#v_ident (#(#recurse)*));
@ -770,18 +812,16 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
// TODO: check first field name is `prefix`? // TODO: check first field name is `prefix`?
let recurse = fields.named.iter().skip(1).map(|f| { let recurse = fields.named.iter().skip(1).map(|f| {
let name = &f.ident; let name = &f.ident;
get_implem_deserialize_for_field(f, quote! { #name: }) get_implem_deserialize_for_field(f, quote! { #name: }, struct_name)
}); });
quote_spanned! { v.span() => quote_spanned! { v.span() =>
return Ok(Self::#v_ident {prefix, #(#recurse)*}); return Ok(Self::#v_ident {prefix, #(#recurse)*});
} }
} }
Fields::Unnamed(ref fields) => { Fields::Unnamed(ref fields) => {
let recurse = fields let recurse = fields.unnamed.iter().skip(1).map(|f| {
.unnamed get_implem_deserialize_for_field(f, quote! {}, struct_name)
.iter() });
.skip(1)
.map(|f| get_implem_deserialize_for_field(f, quote! {}));
quote_spanned! { v.span() => quote_spanned! { v.span() =>
return Ok(Self::#v_ident (prefix, #(#recurse)*)); return Ok(Self::#v_ident (prefix, #(#recurse)*));
} }