senax_encoder_derive/
lib.rs

1extern crate proc_macro;
2
3use crc32fast::Hasher;
4use itertools::izip;
5use proc_macro::TokenStream;
6use proc_macro2::Span;
7use quote::quote;
8use std::collections::{HashMap, HashSet};
9use syn::{
10    parse_macro_input, Attribute, Data, DeriveInput, Fields, GenericArgument, Ident, PathArguments,
11    Type,
12};
13
14/// Calculate ID (CRC32) from name
15///
16/// This function computes a CRC32 hash of the given name string and returns it as a u32.
17/// If the computed hash is 0, it returns 1 instead since 0 is reserved as a terminator.
18fn calculate_id_from_name(name: &str) -> u32 {
19    let mut hasher = Hasher::new();
20    hasher.update(name.as_bytes());
21    let id = hasher.finalize();
22    // If ID is 0, replace with 1 (0 is reserved as a terminator)
23    if id == 0 {
24        1
25    } else {
26        id
27    }
28}
29
30/// Field attributes parsed from `#[senax(...)]` annotations
31///
32/// This struct represents the various attributes that can be applied to fields
33/// in structs and enum variants using the `#[senax(...)]` attribute macro.
34///
35/// # Fields
36///
37/// * `id` - The unique identifier for this field (computed from name or explicitly set)
38/// * `default` - Whether to use default values when the field is missing during decode
39/// * `skip_encode` - Whether to exclude this field from encoding
40/// * `skip_decode` - Whether to ignore this field during decoding
41/// * `skip_default` - Whether to use default value if field is missing
42/// * `rename` - Optional alternative name for ID calculation (maintains compatibility when renaming)
43#[derive(Debug, Clone)]
44#[allow(dead_code)] // The rename field is used indirectly in ID calculation
45struct FieldAttributes {
46    id: u32,
47    default: bool,
48    skip_encode: bool,
49    skip_decode: bool,
50    skip_default: bool,
51    rename: Option<String>,
52}
53
54/// Extract and parse `#[senax(...)]` attribute values from field attributes
55///
56/// This function parses the senax attributes applied to a field and returns
57/// a `FieldAttributes` struct containing all the parsed values.
58///
59/// # Arguments
60///
61/// * `attrs` - The attributes array from the field
62/// * `field_name` - The name of the field (used for ID calculation if no explicit ID is provided)
63///
64/// # Returns
65///
66/// A `FieldAttributes` struct with parsed values. If no explicit ID is provided,
67/// the ID is calculated using CRC32 hash of either the rename value or the field name.
68///
69/// # Supported Attributes
70///
71/// * `#[senax(id=1234)]` - Explicit field ID
72/// * `#[senax(default)]` - Use default value if field is missing during decode
73/// * `#[senax(skip_encode)]` - Skip this field during encoding
74/// * `#[senax(skip_decode)]` - Skip this field during decoding
75/// * `#[senax(skip_default)]` - Skip encoding if field value is default, use default if missing during decode
76/// * `#[senax(rename="name")]` - Alternative name for ID calculation
77///
78/// Multiple attributes can be combined: `#[senax(id=123, default, skip_encode)]`
79fn get_field_attributes(attrs: &[Attribute], field_name: &str) -> FieldAttributes {
80    let mut id = None;
81    let mut default = false;
82    let mut skip_encode = false;
83    let mut skip_decode = false;
84    let mut skip_default = false;
85    let mut rename = None;
86
87    for attr in attrs {
88        if attr.path().is_ident("senax") {
89            // Try to parse #[senax(id=1234, default, skip_encode, skip_decode, skip_default, rename="name")]
90            let parsed = attr.parse_args_with(|input: syn::parse::ParseStream| {
91                let mut parsed_id = None;
92                let mut parsed_default = false;
93                let mut parsed_skip_encode = false;
94                let mut parsed_skip_decode = false;
95                let mut parsed_skip_default = false;
96                let mut parsed_rename = None;
97
98                while !input.is_empty() {
99                    let ident = input.parse::<syn::Ident>()?;
100
101                    if ident == "id" {
102                        input.parse::<syn::Token![=]>()?;
103                        let lit = input.parse::<syn::LitInt>()?;
104                        if let Ok(id_val) = lit.base10_parse::<u32>() {
105                            if id_val == 0 {
106                                return Err(syn::Error::new(
107                                    lit.span(),
108                                    "Field ID 0 is reserved as a terminator",
109                                ));
110                            }
111                            parsed_id = Some(id_val);
112                        } else {
113                            return Err(syn::Error::new(lit.span(), "Failed to parse ID value"));
114                        }
115                    } else if ident == "default" {
116                        parsed_default = true;
117                    } else if ident == "skip_encode" {
118                        parsed_skip_encode = true;
119                    } else if ident == "skip_decode" {
120                        parsed_skip_decode = true;
121                    } else if ident == "skip_default" {
122                        parsed_skip_default = true;
123                    } else if ident == "rename" {
124                        input.parse::<syn::Token![=]>()?;
125                        let lit_str = input.parse::<syn::LitStr>()?;
126                        parsed_rename = Some(lit_str.value());
127                    } else {
128                        return Err(syn::Error::new(
129                            ident.span(),
130                            format!("Unknown attribute: {}", ident),
131                        ));
132                    }
133
134                    // Consume comma if present, otherwise end
135                    if input.peek(syn::Token![,]) {
136                        input.parse::<syn::Token![,]>()?;
137                    }
138                }
139
140                Ok((
141                    parsed_id,
142                    parsed_default,
143                    parsed_skip_encode,
144                    parsed_skip_decode,
145                    parsed_skip_default,
146                    parsed_rename,
147                ))
148            });
149
150            if let Ok((
151                parsed_id,
152                parsed_default,
153                parsed_skip_encode,
154                parsed_skip_decode,
155                parsed_skip_default,
156                parsed_rename,
157            )) = parsed
158            {
159                if let Some(id_val) = parsed_id {
160                    id = Some(id_val);
161                }
162                default = default || parsed_default;
163                skip_encode = skip_encode || parsed_skip_encode;
164                skip_decode = skip_decode || parsed_skip_decode;
165                skip_default = skip_default || parsed_skip_default;
166                if let Some(rename_val) = parsed_rename {
167                    rename = Some(rename_val);
168                }
169            } else {
170                eprintln!(
171                    "Warning: #[senax(...)] attribute for field '{}' is not in the correct format.",
172                    field_name
173                );
174            }
175        }
176    }
177
178    // ID calculation: Use explicit ID if provided, otherwise calculate CRC32 from rename or field name
179    let calculated_id = id.unwrap_or_else(|| {
180        let name_for_id = if let Some(ref rename_val) = rename {
181            rename_val.as_str()
182        } else {
183            field_name
184        };
185        calculate_id_from_name(name_for_id)
186    });
187
188    FieldAttributes {
189        id: calculated_id,
190        default,
191        skip_encode,
192        skip_decode,
193        skip_default,
194        rename,
195    }
196}
197
198/// Check if a type is `Option<T>`
199///
200/// This helper function determines whether a given type is wrapped in an `Option`.
201fn is_option_type(ty: &Type) -> bool {
202    if let Type::Path(type_path) = ty {
203        type_path
204            .path
205            .segments
206            .last()
207            .map_or(false, |seg| seg.ident == "Option")
208    } else {
209        false
210    }
211}
212
213/// Extract the inner type `T` from `Option<T>`
214///
215/// This helper function extracts the wrapped type from an `Option` type.
216/// Returns `None` if the type is not an `Option`.
217fn extract_inner_type_from_option(ty: &Type) -> Option<&Type> {
218    if let Type::Path(type_path) = ty {
219        if type_path
220            .path
221            .segments
222            .last()
223            .map_or(false, |seg| seg.ident == "Option")
224        {
225            if let PathArguments::AngleBracketed(args) =
226                &type_path.path.segments.last().unwrap().arguments
227            {
228                if let Some(GenericArgument::Type(inner_ty)) = args.args.first() {
229                    return Some(inner_ty);
230                }
231            }
232        }
233    }
234    None
235}
236
237/// Extract and parse container-level #[senax(...)] attribute values (e.g. #[senax(u8)])
238fn get_container_attributes(attrs: &[Attribute]) -> bool {
239    for attr in attrs {
240        if attr.path().is_ident("senax") {
241            let parsed = attr.parse_args_with(|input: syn::parse::ParseStream| {
242                let mut id_u8 = false;
243                while !input.is_empty() {
244                    let ident = input.parse::<syn::Ident>()?;
245                    if ident == "u8" {
246                        id_u8 = true;
247                    }
248                    if input.peek(syn::Token![,]) {
249                        input.parse::<syn::Token![,]>()?;
250                    }
251                }
252                Ok(id_u8)
253            });
254            if let Ok(id_u8) = parsed {
255                if id_u8 {
256                    return true;
257                }
258            }
259        }
260    }
261    false
262}
263
264/// Derive macro for implementing the `Encode` trait
265///
266/// This procedural macro automatically generates an implementation of the `Encode` trait
267/// for structs and enums. It supports various field attributes for customizing the
268/// encoding behavior.
269///
270/// # Supported Attributes
271///
272/// * `#[senax(id=N)]` - Set explicit field/variant ID
273/// * `#[senax(skip_encode)]` - Skip field during encoding
274/// * `#[senax(rename="name")]` - Use alternative name for ID calculation
275///
276/// # Examples
277///
278/// ```rust
279/// #[derive(Encode)]
280/// struct MyStruct {
281///     #[senax(id=1)]
282///     field1: i32,
283///     #[senax(skip_encode)]
284///     field2: String,
285/// }
286/// ```
287#[proc_macro_derive(Encode, attributes(senax))]
288pub fn derive_encode(input: TokenStream) -> TokenStream {
289    let input = parse_macro_input!(input as DeriveInput);
290    let name = &input.ident;
291    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
292    let container_id_u8 = get_container_attributes(&input.attrs);
293
294    let encode_fields = match &input.data {
295        Data::Struct(s) => match &s.fields {
296            Fields::Named(fields) => {
297                let mut field_encode = Vec::new();
298                let mut used_ids_struct = HashSet::new();
299                for f in &fields.named {
300                    let field_name_str = f.ident.as_ref().unwrap().to_string();
301                    let field_attrs = get_field_attributes(&f.attrs, &field_name_str);
302
303                    // Skip fields marked with skip_encode
304                    if field_attrs.skip_encode {
305                        continue;
306                    }
307
308                    if !used_ids_struct.insert(field_attrs.id) {
309                        panic!("Field ID (0x{:08X}) is duplicated for struct '{}'. Please specify a different ID for field '{}' using #[senax(id=...)].", field_attrs.id, name, field_name_str);
310                    }
311
312                    let field_ident = &f.ident;
313                    let ty = &f.ty;
314                    let is_option = is_option_type(ty);
315                    let field_id = field_attrs.id;
316
317                    if is_option {
318                        if container_id_u8 {
319                            field_encode.push(quote! {
320                                if let Some(val) = &self.#field_ident {
321                                    let field_id_val: u8 = #field_id as u8;
322                                    writer.put_u8(field_id_val);
323                                    val.encode(writer)?;
324                                }
325                            });
326                        } else {
327                            field_encode.push(quote! {
328                                if let Some(val) = &self.#field_ident {
329                                    let field_id_val: u32 = #field_id;
330                                    if #container_id_u8 {
331                                        writer.put_u8(field_id_val as u8);
332                                    } else {
333                                        senax_encoder::write_u32_le(writer, field_id_val)?;
334                                    }
335                                    val.encode(writer)?;
336                                }
337                            });
338                        }
339                    } else if field_attrs.skip_default {
340                        // For skip_default fields, check if the value is default before encoding
341                        if container_id_u8 {
342                            field_encode.push(quote! {
343                                if !self.#field_ident.is_default() {
344                                    let field_id_val: u8 = #field_id as u8;
345                                    writer.put_u8(field_id_val);
346                                    self.#field_ident.encode(writer)?;
347                                }
348                            });
349                        } else {
350                            field_encode.push(quote! {
351                                if !self.#field_ident.is_default() {
352                                    let field_id_val: u32 = #field_id;
353                                    if #container_id_u8 {
354                                        writer.put_u8(field_id_val as u8);
355                                    } else {
356                                        senax_encoder::write_u32_le(writer, field_id_val)?;
357                                    }
358                                    self.#field_ident.encode(writer)?;
359                                }
360                            });
361                        }
362                    } else if container_id_u8 {
363                        field_encode.push(quote! {
364                            let field_id_val: u8 = #field_id as u8;
365                            writer.put_u8(field_id_val);
366                            self.#field_ident.encode(writer)?;
367                        });
368                    } else {
369                        field_encode.push(quote! {
370                            let field_id_val: u32 = #field_id;
371                            if #container_id_u8 {
372                                writer.put_u8(field_id_val as u8);
373                            } else {
374                                senax_encoder::write_u32_le(writer, field_id_val)?;
375                            }
376                            self.#field_ident.encode(writer)?;
377                        });
378                    }
379                }
380                if container_id_u8 {
381                    quote! {
382                        writer.put_u8(senax_encoder::TAG_STRUCT_NAMED);
383                        #(#field_encode)*
384                        writer.put_u8(0);
385                    }
386                } else {
387                    quote! {
388                        writer.put_u8(senax_encoder::TAG_STRUCT_NAMED);
389                        #(#field_encode)*
390                        senax_encoder::write_u32_le(writer, 0)?;
391                    }
392                }
393            }
394            Fields::Unnamed(fields) => {
395                let field_count = fields.unnamed.len();
396                let field_encode = fields.unnamed.iter().enumerate().map(|(i, _)| {
397                    let index = syn::Index::from(i);
398                    quote! {
399                        self.#index.encode(writer)?;
400                    }
401                });
402                quote! {
403                    writer.put_u8(senax_encoder::TAG_STRUCT_UNNAMED);
404                    let count: usize = #field_count;
405                    count.encode(writer)?;
406                    #(#field_encode)*
407                }
408            }
409            Fields::Unit => quote! {
410                writer.put_u8(senax_encoder::TAG_STRUCT_UNIT);
411            },
412        },
413        Data::Enum(e) => {
414            let mut variant_encode = Vec::new();
415            let mut used_ids_enum = HashSet::new();
416
417            for v in &e.variants {
418                let variant_name_str = v.ident.to_string();
419                let variant_attrs = get_field_attributes(&v.attrs, &variant_name_str);
420                let variant_id = variant_attrs.id;
421
422                if !used_ids_enum.insert(variant_id) {
423                    panic!("Variant ID (0x{:08X}) is duplicated for enum '{}'. Please specify a different ID for variant '{}' using #[senax(id=...)].", variant_id, name, variant_name_str);
424                }
425
426                let variant_ident = &v.ident;
427                match &v.fields {
428                    Fields::Named(fields) => {
429                        let field_idents: Vec<_> = fields
430                            .named
431                            .iter()
432                            .map(|f| f.ident.as_ref().unwrap())
433                            .collect();
434                        let mut field_encode = Vec::new();
435                        let mut used_ids_struct = HashSet::new();
436                        for f in &fields.named {
437                            let field_name_str = f.ident.as_ref().unwrap().to_string();
438                            let field_attrs = get_field_attributes(&f.attrs, &field_name_str);
439
440                            // Skip fields marked with skip_encode
441                            if field_attrs.skip_encode {
442                                continue;
443                            }
444
445                            if !used_ids_struct.insert(field_attrs.id) {
446                                panic!("Field ID (0x{:08X}) is duplicated for enum variant '{}'. Please specify a different ID for field '{}' using #[senax(id=...)].", field_attrs.id, variant_ident, field_name_str);
447                            }
448                            let field_ident = &f.ident;
449                            let ty = &f.ty;
450                            let is_option = is_option_type(ty);
451                            let field_id = field_attrs.id;
452                            if is_option {
453                                field_encode.push(quote! {
454                                    if let Some(val) = #field_ident {
455                                        let field_id_val: u32 = #field_id;
456                                        if #container_id_u8 {
457                                            writer.put_u8(field_id_val as u8);
458                                        } else {
459                                            senax_encoder::write_u32_le(writer, field_id_val)?;
460                                        }
461                                        val.encode(writer)?;
462                                    }
463                                });
464                            } else if field_attrs.skip_default {
465                                // For skip_default fields, check if the value is default before encoding
466                                field_encode.push(quote! {
467                                    if !#field_ident.is_default() {
468                                        let field_id_val: u32 = #field_id;
469                                        if #container_id_u8 {
470                                            writer.put_u8(field_id_val as u8);
471                                        } else {
472                                            senax_encoder::write_u32_le(writer, field_id_val)?;
473                                        }
474                                        #field_ident.encode(writer)?;
475                                    }
476                                });
477                            } else {
478                                field_encode.push(quote! {
479                                    let field_id_val: u32 = #field_id;
480                                    if #container_id_u8 {
481                                        writer.put_u8(field_id_val as u8);
482                                    } else {
483                                        senax_encoder::write_u32_le(writer, field_id_val)?;
484                                    }
485                                    #field_ident.encode(writer)?;
486                                });
487                            }
488                        }
489                        if container_id_u8 {
490                            variant_encode.push(quote! {
491                                #name::#variant_ident { #(#field_idents),* } => {
492                                    writer.put_u8(senax_encoder::TAG_ENUM_NAMED);
493                                    let variant_id_val: u8 = #variant_id as u8;
494                                    writer.put_u8(variant_id_val);
495                                    #(#field_encode)*
496                                    writer.put_u8(0);
497                                }
498                            });
499                        } else {
500                            variant_encode.push(quote! {
501                                #name::#variant_ident { #(#field_idents),* } => {
502                                    writer.put_u8(senax_encoder::TAG_ENUM_NAMED);
503                                    let variant_id_val: u32 = #variant_id;
504                                    if #container_id_u8 {
505                                        writer.put_u8(variant_id_val as u8);
506                                    } else {
507                                        senax_encoder::write_u32_le(writer, variant_id_val)?;
508                                    }
509                                    #(#field_encode)*
510                                    senax_encoder::write_u32_le(writer, 0)?;
511                                }
512                            });
513                        }
514                    }
515                    Fields::Unnamed(fields) => {
516                        let field_count = fields.unnamed.len();
517                        let field_bindings: Vec<_> = (0..field_count)
518                            .map(|i| Ident::new(&format!("field{}", i), Span::call_site()))
519                            .collect();
520                        let field_bindings_ref = &field_bindings;
521                        variant_encode.push(quote! {
522                            #name::#variant_ident( #(#field_bindings_ref),* ) => {
523                                writer.put_u8(senax_encoder::TAG_ENUM_UNNAMED);
524                                let variant_id_val: u32 = #variant_id;
525                                if #container_id_u8 {
526                                    writer.put_u8(variant_id_val as u8);
527                                } else {
528                                    senax_encoder::write_u32_le(writer, variant_id_val)?;
529                                }
530                                let count: usize = #field_count;
531                                count.encode(writer)?;
532                                #(
533                                    #field_bindings_ref.encode(writer)?;
534                                )*
535                            }
536                        });
537                    }
538                    Fields::Unit => {
539                        variant_encode.push(quote! {
540                            #name::#variant_ident => {
541                                writer.put_u8(senax_encoder::TAG_ENUM);
542                                let variant_id_val: u32 = #variant_id;
543                                if #container_id_u8 {
544                                    writer.put_u8(variant_id_val as u8);
545                                } else {
546                                    senax_encoder::write_u32_le(writer, variant_id_val)?;
547                                }
548                            }
549                        });
550                    }
551                }
552            }
553            quote! {
554                match self {
555                    #(#variant_encode)*
556                }
557            }
558        }
559        Data::Union(_) => unimplemented!("Unions are not supported"),
560    };
561
562    TokenStream::from(quote! {
563        impl #impl_generics senax_encoder::Encoder for #name #ty_generics #where_clause {
564            fn encode(&self, writer: &mut bytes::BytesMut) -> senax_encoder::Result<()> {
565                use bytes::{Buf, BufMut};
566                #encode_fields
567                Ok(())
568            }
569
570            fn is_default(&self) -> bool {
571                false // Structs and enums always return false
572            }
573        }
574    })
575}
576
577/// Derive macro for implementing the `Decode` trait
578///
579/// This procedural macro automatically generates an implementation of the `Decode` trait
580/// for structs and enums. It supports various field attributes for customizing the
581/// decoding behavior and provides forward/backward compatibility.
582///
583/// # Supported Attributes
584///
585/// * `#[senax(id=N)]` - Set explicit field/variant ID
586/// * `#[senax(default)]` - Use default value if field is missing
587/// * `#[senax(skip_decode)]` - Skip field during decoding (use default value)
588/// * `#[senax(skip_default)]` - Use default value if field is missing (same as default for decode)
589/// * `#[senax(rename="name")]` - Use alternative name for ID calculation
590///
591/// # Examples
592///
593/// ```rust
594/// #[derive(Decode)]
595/// struct MyStruct {
596///     #[senax(id=1)]
597///     field1: i32,
598///     #[senax(default)]
599///     field2: String,
600///     #[senax(skip_decode)]
601///     field3: bool,
602/// }
603/// ```
604#[proc_macro_derive(Decode, attributes(senax))]
605pub fn derive_decode(input: TokenStream) -> TokenStream {
606    let input = parse_macro_input!(input as DeriveInput);
607    let name = &input.ident;
608    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
609    let container_id_u8 = get_container_attributes(&input.attrs);
610
611    let decode_fields = match &input.data {
612        Data::Struct(s) => match &s.fields {
613            Fields::Named(fields) => {
614                let mut field_idents = Vec::new();
615                let mut field_original_types = Vec::new();
616                let mut field_ids_for_match = Vec::new();
617                let mut field_is_option_flags = Vec::new();
618                let mut field_attrs_list = Vec::new();
619                let mut used_ids_struct_decode = HashMap::new();
620
621                for f in &fields.named {
622                    let field_name_str = f.ident.as_ref().unwrap().to_string();
623                    let field_attrs = get_field_attributes(&f.attrs, &field_name_str);
624
625                    if let Some(dup_field_name) =
626                        used_ids_struct_decode.insert(field_attrs.id, field_name_str.clone())
627                    {
628                        panic!("Field ID (0x{:08X}) is duplicated for struct '{}'. Please specify a different ID for field '{}' and '{}' using #[senax(id=...)].", 
629                              field_attrs.id, name, dup_field_name, field_name_str);
630                    }
631
632                    field_idents.push(f.ident.as_ref().unwrap().clone());
633                    field_original_types.push(f.ty.clone());
634                    field_ids_for_match.push(field_attrs.id);
635                    field_is_option_flags.push(is_option_type(&f.ty));
636                    field_attrs_list.push(field_attrs);
637                }
638
639                let field_value_definitions = field_idents
640                    .iter()
641                    .zip(field_original_types.iter())
642                    .zip(field_attrs_list.iter())
643                    .filter_map(|((ident, original_ty), attrs)| {
644                        if attrs.skip_decode {
645                            // Fields marked with skip_decode don't store values
646                            None
647                        } else if is_option_type(original_ty) {
648                            Some(quote! { #ident: #original_ty, })
649                        } else {
650                            Some(quote! { #ident: Option<#original_ty>, })
651                        }
652                    });
653
654                let match_arms = field_idents
655                    .iter()
656                    .zip(field_original_types.iter())
657                    .zip(field_ids_for_match.iter())
658                    .zip(field_attrs_list.iter())
659                    .filter_map(|(((ident, original_ty), id_val), attrs)| {
660                        if attrs.skip_decode {
661                            // Fields marked with skip_decode don't generate match arms (values are skipped)
662                            None
663                        } else if is_option_type(original_ty) {
664                            let inner_ty = extract_inner_type_from_option(original_ty)
665                                .unwrap_or_else(|| {
666                                    panic!(
667                                        "Failed to extract inner type from Option for field {}",
668                                        ident
669                                    )
670                                });
671                            Some(quote! {
672                                x if x == #id_val => {
673                                    field_values.#ident = Some(<#inner_ty>::decode(reader)?);
674                                }
675                            })
676                        } else {
677                            Some(quote! {
678                                x if x == #id_val => {
679                                    field_values.#ident = Some(<#original_ty>::decode(reader)?);
680                                }
681                            })
682                        }
683                    });
684
685                let struct_assignments = field_idents.iter()
686                    .zip(field_is_option_flags.iter())
687                    .zip(field_attrs_list.iter())
688                    .map(|((ident, is_opt_flag), attrs)| {
689                        if attrs.skip_decode {
690                            // Fields marked with skip_decode use default values
691                            quote! {
692                                #ident: Default::default(),
693                            }
694                        } else if *is_opt_flag {
695                            quote! {
696                                #ident: field_values.#ident,
697                            }
698                        } else if attrs.default || attrs.skip_default {
699                            // Fields marked with default or skip_default use default value if missing
700                            quote! {
701                                #ident: field_values.#ident.unwrap_or_default(),
702                            }
703                        } else {
704                            quote! {
705                                #ident: field_values.#ident.ok_or_else(||
706                                    senax_encoder::EncoderError::Decode(format!("Required field '{}' not found for struct {}", stringify!(#ident), stringify!(#name)))
707                                )?,
708                            }
709                        }
710                    });
711
712                quote! {
713                    if reader.remaining() == 0 {
714                        return Err(senax_encoder::EncoderError::InsufficientData);
715                    }
716                    let tag = reader.get_u8();
717                    if tag != senax_encoder::TAG_STRUCT_NAMED {
718                        return Err(senax_encoder::EncoderError::Decode(format!("Expected struct named tag ({}), got {}", senax_encoder::TAG_STRUCT_NAMED, tag)));
719                    }
720
721                    #[derive(Default)]
722                    struct FieldValues {
723                        #( #field_value_definitions )*
724                    }
725
726                    let mut field_values = FieldValues::default();
727
728                    loop {
729                        let field_id = if #container_id_u8 {
730                            if reader.remaining() == 0 { break; }
731                            let id = reader.get_u8() as u32;
732                            if id == 0 { break; }
733                            id
734                        } else {
735                            let id = senax_encoder::read_u32_le(reader)?;
736                            if id == 0 { break; }
737                            id
738                        };
739                        match field_id {
740                            #( #match_arms )*
741                            _unknown_id => { senax_encoder::skip_value(reader)?; }
742                        }
743                    }
744
745                    Ok(#name {
746                        #( #struct_assignments )*
747                    })
748                }
749            }
750            Fields::Unnamed(fields) => {
751                let field_count = fields.unnamed.len();
752                let field_deencode = fields.unnamed.iter().map(|f| {
753                    let field_ty = &f.ty;
754                    quote! {
755                        <#field_ty>::decode(reader)?
756                    }
757                });
758                quote! {
759                    if reader.remaining() == 0 {
760                        return Err(senax_encoder::EncoderError::InsufficientData);
761                    }
762                    let tag = reader.get_u8();
763                    if tag != senax_encoder::TAG_STRUCT_UNNAMED {
764                        return Err(senax_encoder::EncoderError::Decode(format!("Expected struct unnamed tag ({}), got {}", senax_encoder::TAG_STRUCT_UNNAMED, tag)));
765                    }
766                    let count = <usize>::decode(reader)?;
767                    if count != #field_count {
768                        return Err(senax_encoder::EncoderError::Decode(format!("Field count mismatch for struct {}: expected {}, got {}", stringify!(#name), #field_count, count)));
769                    }
770                    Ok(#name(
771                        #(#field_deencode),*
772                    ))
773                }
774            }
775            Fields::Unit => quote! {
776                if reader.remaining() == 0 {
777                    return Err(senax_encoder::EncoderError::InsufficientData);
778                }
779                let tag = reader.get_u8();
780                if tag != senax_encoder::TAG_STRUCT_UNIT {
781                    return Err(senax_encoder::EncoderError::Decode(format!("Expected struct unit tag ({}), got {}", senax_encoder::TAG_STRUCT_UNIT, tag)));
782                }
783                Ok(#name)
784            },
785        },
786        Data::Enum(e) => {
787            let mut unit_variant_arms = Vec::new();
788            let mut named_variant_arms = Vec::new();
789            let mut unnamed_variant_arms = Vec::new();
790            let mut used_ids_enum_decode = HashMap::new();
791
792            for v in &e.variants {
793                let variant_name_str = v.ident.to_string();
794                let variant_attrs = get_field_attributes(&v.attrs, &variant_name_str);
795                let variant_id = variant_attrs.id;
796
797                if let Some(dup_variant) =
798                    used_ids_enum_decode.insert(variant_id, variant_name_str.clone())
799                {
800                    panic!("Variant ID (0x{:08X}) is duplicated for enum '{}'. Please specify a different ID for variant '{}' and '{}' using #[senax(id=...)].", 
801                          variant_id, name, dup_variant, variant_name_str);
802                }
803
804                let variant_ident = &v.ident;
805                match &v.fields {
806                    Fields::Named(fields) => {
807                        let field_idents: Vec<_> = fields
808                            .named
809                            .iter()
810                            .map(|f| f.ident.as_ref().unwrap().clone())
811                            .collect();
812                        let field_types: Vec<_> =
813                            fields.named.iter().map(|f| f.ty.clone()).collect();
814                        let field_attrs_list: Vec<_> = fields
815                            .named
816                            .iter()
817                            .map(|f| {
818                                get_field_attributes(
819                                    &f.attrs,
820                                    &f.ident.as_ref().unwrap().to_string(),
821                                )
822                            })
823                            .collect();
824
825                        let mut field_value_definitions_enum = Vec::new();
826                        let mut match_arms_enum_named = Vec::new();
827                        let mut struct_assignments_enum_named = Vec::new();
828
829                        for (ident, ty, attrs) in izip!(
830                            field_idents.iter(),
831                            field_types.iter(),
832                            field_attrs_list.iter()
833                        ) {
834                            if attrs.skip_decode {
835                                // Fields marked with skip_decode don't store values
836                            } else if is_option_type(ty) {
837                                field_value_definitions_enum.push(quote! { #ident: #ty, });
838                            } else {
839                                field_value_definitions_enum.push(quote! { #ident: Option<#ty>, });
840                            }
841
842                            if attrs.skip_decode {
843                                // Fields marked with skip_decode don't generate match arms
844                            } else if is_option_type(ty) {
845                                let inner_ty = extract_inner_type_from_option(ty).unwrap();
846                                let field_id = attrs.id;
847                                match_arms_enum_named.push(quote! {
848                                    x if x == #field_id => { field_values.#ident = Some(<#inner_ty>::decode(reader)?); }
849                                });
850                            } else {
851                                let field_id = attrs.id;
852                                match_arms_enum_named.push(quote! {
853                                    x if x == #field_id => { field_values.#ident = Some(<#ty>::decode(reader)?); }
854                                });
855                            }
856
857                            if attrs.skip_decode {
858                                // Fields marked with skip_decode use default values
859                                struct_assignments_enum_named
860                                    .push(quote! { #ident: Default::default(), });
861                            } else if is_option_type(ty) {
862                                struct_assignments_enum_named
863                                    .push(quote! { #ident: field_values.#ident, });
864                            } else if attrs.default || attrs.skip_default {
865                                // Fields marked with default or skip_default use default value if missing
866                                struct_assignments_enum_named.push(quote! {
867                                    #ident: field_values.#ident.unwrap_or_default(),
868                                });
869                            } else {
870                                struct_assignments_enum_named.push(quote! {
871                                    #ident: field_values.#ident.ok_or_else(|| senax_encoder::EncoderError::Decode(format!("Required field '{}' not found for variant {}::{}", stringify!(#ident), stringify!(#name), stringify!(#variant_ident))))?,
872                                });
873                            }
874                        }
875
876                        named_variant_arms.push(quote! {
877                            x if x == #variant_id => {
878                                #[derive(Default)]
879                                struct FieldValues { #(#field_value_definitions_enum)* }
880                                let mut field_values = FieldValues::default();
881                                loop {
882                                    let field_id = if #container_id_u8 {
883                                        if reader.remaining() == 0 { break; }
884                                        let id = reader.get_u8() as u32;
885                                        if id == 0 { break; }
886                                        id
887                                    } else {
888                                        let id = senax_encoder::read_u32_le(reader)?;
889                                        if id == 0 { break; }
890                                        id
891                                    };
892                                    match field_id {
893                                        #(#match_arms_enum_named)*
894                                        _unknown_id => { senax_encoder::skip_value(reader)?; }
895                                    }
896                                }
897                                Ok(#name::#variant_ident { #(#struct_assignments_enum_named)* })
898                            }
899                        });
900                    }
901                    Fields::Unnamed(fields) => {
902                        let field_types: Vec<_> = fields.unnamed.iter().map(|f| &f.ty).collect();
903                        let field_count = field_types.len();
904                        unnamed_variant_arms.push(quote! {
905                            x if x == #variant_id => {
906                                let count = <usize>::decode(reader)?;
907                                if count != #field_count {
908                                    return Err(senax_encoder::EncoderError::Decode(format!("Field count mismatch for variant {}::{}: expected {}, got {}", stringify!(#name), stringify!(#variant_ident), #field_count, count)));
909                                }
910                                Ok(#name::#variant_ident(
911                                    #(
912                                        <#field_types>::decode(reader)?,
913                                    )*
914                                ))
915                            }
916                        });
917                    }
918                    Fields::Unit => {
919                        unit_variant_arms.push(quote! {
920                            x if x == #variant_id => {
921                                Ok(#name::#variant_ident)
922                            }
923                        });
924                    }
925                }
926            }
927            quote! {
928                if reader.remaining() == 0 {
929                    return Err(senax_encoder::EncoderError::InsufficientData);
930                }
931                let tag = reader.get_u8();
932                match tag {
933                    senax_encoder::TAG_ENUM => {
934                        let variant_id = if #container_id_u8 {
935                            reader.get_u8() as u32
936                        } else {
937                            senax_encoder::read_u32_le(reader)?
938                        };
939                        match variant_id {
940                            #(#unit_variant_arms)*
941                            _ => Err(senax_encoder::EncoderError::Decode(format!("Unknown unit variant ID: 0x{:08X} for enum {}", variant_id, stringify!(#name))))
942                        }
943                    }
944                    senax_encoder::TAG_ENUM_NAMED => {
945                        let variant_id = if #container_id_u8 {
946                            reader.get_u8() as u32
947                        } else {
948                            senax_encoder::read_u32_le(reader)?
949                        };
950                        match variant_id {
951                            #(#named_variant_arms)*
952                            _ => Err(senax_encoder::EncoderError::Decode(format!("Unknown named variant ID: 0x{:08X} for enum {}", variant_id, stringify!(#name))))
953                        }
954                    }
955                    senax_encoder::TAG_ENUM_UNNAMED => {
956                        let variant_id = if #container_id_u8 {
957                            reader.get_u8() as u32
958                        } else {
959                            senax_encoder::read_u32_le(reader)?
960                        };
961                        match variant_id {
962                             #(#unnamed_variant_arms)*
963                            _ => Err(senax_encoder::EncoderError::Decode(format!("Unknown unnamed variant ID: 0x{:08X} for enum {}", variant_id, stringify!(#name))))
964                        }
965                    }
966                    unknown_tag => Err(senax_encoder::EncoderError::Decode(format!("Unknown enum tag: {} for enum {}", unknown_tag, stringify!(#name))))
967                }
968            }
969        }
970        Data::Union(_) => unimplemented!("Unions are not supported"),
971    };
972
973    TokenStream::from(quote! {
974        impl #impl_generics senax_encoder::Decoder for #name #ty_generics #where_clause {
975            fn decode(reader: &mut bytes::Bytes) -> senax_encoder::Result<Self> {
976                use bytes::{Buf, BufMut};
977                #decode_fields
978            }
979        }
980    })
981}