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::spanned::Spanned;
use syn::{
Attribute, Data, DeriveInput, Field, Fields, Ident, Index, Meta, MetaList, Token, Type,
Variant, parse_macro_input,
parse_macro_input, Attribute, Data, DeriveInput, Field, Fields, Ident, Index, Meta, MetaList,
Token, Type, Variant,
};
/// 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 name = input.ident;
let params = ParamsStruct::parse(&input.attrs);
let implem_serialize = get_implem_serialize(&input.data, &params);
let implem_deserialize = get_implem_deserialize(&input.data, &params);
let implem_size = get_implem_size(&input.data, &params);
let implem_serialize = get_implem_serialize(&input.data, &params, &name);
let implem_deserialize = get_implem_deserialize(&input.data, &params, &name);
let implem_size = get_implem_size(&input.data, &params, &name);
let expanded = quote! {
impl androscalpel_serializer::Serializable for #name {
#[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`]
/// 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 prefix_stream = match params.prefix {
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`].
fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
fn get_implem_size(data: &Data, params: &ParamsStruct, struct_name: &Ident) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
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! {
0 #(+ #recurse)*
@ -374,7 +378,7 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
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! {
0 #(+ #recurse)*
@ -404,7 +408,7 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
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() =>
//<#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) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
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() =>
//<#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`]
/// 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);
// TODO: Improve error handling
let ident = &f.ident;
let prefix_stream = match params.prefix {
Some(PrefixParamsField(ref stream)) => {
quote_spanned! { f.span() =>
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)) => {
quote_spanned! { f.span() =>
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() =>
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() =>
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() =>
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() =>
@ -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`].
fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
fn get_implem_serialize(data: &Data, params: &ParamsStruct, struct_name: &Ident) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
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! {
#(#recurse)*
@ -508,7 +536,7 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
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! {
#(#recurse)*
@ -552,7 +580,7 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
}
let recurse = fields.named.iter().map(|f| {
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() =>
//<#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) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
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() =>
//<#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`]
/// 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 ty = &f.ty;
// TODO: Improve error handling
let ident = &f.ident;
let prefix_stream = match params.prefix {
Some(PrefixParamsField(ref stream)) => {
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()
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 {
if <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte {
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() =>
#field_ref {
#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
val
},
@ -653,7 +689,13 @@ fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenS
#prefix_stream
let mut vec_ = vec![];
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
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`].
fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
fn get_implem_deserialize(data: &Data, params: &ParamsStruct, struct_name: &Ident) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
get_implem_deserialize_for_field(f, quote! { #name: })
get_implem_deserialize_for_field(f, quote! { #name: }, struct_name)
});
quote! {
Ok(
@ -692,7 +734,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
let recurse = fields
.unnamed
.iter()
.map(|f| get_implem_deserialize_for_field(f, quote! {}));
.map(|f| get_implem_deserialize_for_field(f, quote! {}, struct_name));
quote! {
Ok(Self(#(#recurse)*))
}
@ -717,7 +759,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
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() =>
if #val == prefix {
@ -729,7 +771,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
let recurse = fields
.unnamed
.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() =>
if #val == prefix {
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`?
let recurse = fields.named.iter().skip(1).map(|f| {
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() =>
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! {}));
let recurse = fields.unnamed.iter().skip(1).map(|f| {
get_implem_deserialize_for_field(f, quote! {}, struct_name)
});
quote_spanned! { v.span() =>
return Ok(Self::#v_ident (prefix, #(#recurse)*));
}