From 4b1cc379a4dc2f30ceeb168151354c9b17eb46df Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Mon, 12 Jan 2026 23:16:40 +0100 Subject: [PATCH] add better errors --- androscalpel_serializer_derive/src/lib.rs | 120 ++++++++++++++-------- 1 file changed, 80 insertions(+), 40 deletions(-) diff --git a/androscalpel_serializer_derive/src/lib.rs b/androscalpel_serializer_derive/src/lib.rs index 8ccfd3c..4c48c05 100644 --- a/androscalpel_serializer_derive/src/lib.rs +++ b/androscalpel_serializer_derive/src/lib.rs @@ -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, ¶ms); - let implem_deserialize = get_implem_deserialize(&input.data, ¶ms); - let implem_size = get_implem_size(&input.data, ¶ms); + let implem_serialize = get_implem_serialize(&input.data, ¶ms, &name); + let implem_deserialize = get_implem_deserialize(&input.data, ¶ms, &name); + let implem_size = get_implem_size(&input.data, ¶ms, &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 { - ::serialize(&byte, output)?; + ::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 { - ::serialize(&byte, output)?; + ::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 ::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 ::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)*)); }