Skip to main content

binary_codec_derive/
lib.rs

1extern crate proc_macro;
2
3use quote::{format_ident, quote};
4use syn::{
5    Attribute, Data, DeriveInput, Fields, Lit, PathArguments, Type, parse_macro_input,
6    punctuated::Punctuated, token::Comma,
7};
8
9#[proc_macro_derive(
10    ToBytes,
11    attributes(
12        bits,
13        dyn_int,
14        dyn_length,
15        key_dyn_length,
16        val_dyn_length,
17        toggles,
18        toggled_by,
19        toggled_by_variant,
20        length_for,
21        length_by,
22        variant_for,
23        variant_by,
24        multi_enum,
25        no_discriminator,
26        codec_error,
27        codec_ser_error,
28        codec_de_error,
29    )
30)]
31pub fn generate_code_to_bytes(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
32    generate_code_binary_serializer(false, input)
33}
34
35#[proc_macro_derive(
36    FromBytes,
37    attributes(
38        bits,
39        dyn_int,
40        key_dyn_length,
41        val_dyn_length,
42        dyn_length,
43        toggles,
44        toggled_by,
45        toggled_by_variant,
46        length_for,
47        length_by,
48        variant_for,
49        variant_by,
50        multi_enum,
51        no_discriminator,
52        codec_error,
53        codec_ser_error,
54        codec_de_error,
55    )
56)]
57pub fn generate_code_from_bytes(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
58    generate_code_binary_serializer(true, input)
59}
60
61fn generate_code_binary_serializer(
62    read: bool,
63    input: proc_macro::TokenStream,
64) -> proc_macro::TokenStream {
65    // Parse code input (TokenStream) to AST
66    let ast = parse_macro_input!(input as DeriveInput);
67
68    match ast.data {
69        Data::Struct(ref data) => generate_struct_serializer(read, &ast, data),
70        Data::Enum(ref data) => generate_enum_serializer(read, &ast, data),
71        _ => panic!("ToBytes can only be used on structs"),
72    }
73}
74
75fn generate_field_serializer(
76    read: bool,
77    field_ident: &proc_macro2::Ident,
78    field_type: &syn::Type,
79    field: &syn::Field,
80    is_enum: bool,
81) -> proc_macro2::TokenStream {
82    let single_ident_type_name = if let Type::Path(path) = field_type {
83        if path.path.segments.len() == 1 {
84            Some(path.path.segments[0].ident.to_string())
85        } else {
86            None
87        }
88    } else {
89        None
90    };
91
92    let mut toggle_key = None;
93    // allow multiple variant_for / length_for entries
94    let mut variant_keys: Vec<String> = Vec::new();
95    let mut length_keys: Vec<String> = Vec::new();
96    let mut toggled_by_variant = None;
97    let mut toggled_by = None;
98    let mut variant_by = None;
99    let mut length_by = None;
100    let mut is_dynamic_int = false;
101    let mut has_dynamic_length = false;
102    let mut bits_count = None;
103    let mut key_dyn_length = false;
104    let mut val_dyn_length = false;
105    let mut multi_enum = false;
106
107    // Search attributes for length/toggle declarations
108    for attr in field.attrs.iter() {
109        let ident = attr.path().get_ident().map(|i| i.clone().to_string());
110        match ident.as_deref() {
111            Some("dyn_int") => is_dynamic_int = true,
112            Some("dyn_length") => has_dynamic_length = true,
113            Some("key_dyn_length") => key_dyn_length = true,
114            Some("val_dyn_length") => val_dyn_length = true,
115            Some("multi_enum") => multi_enum = true,
116            Some("toggles") => toggle_key = get_string_value_from_attribute(attr),
117            Some("variant_for") => {
118                if let Some(v) = get_string_value_from_attribute(attr) {
119                    variant_keys.push(v);
120                }
121            }
122            Some("length_for") => {
123                if let Some(v) = get_string_value_from_attribute(attr) {
124                    length_keys.push(v);
125                }
126            }
127            Some("toggled_by") => toggled_by = get_string_value_from_attribute(attr),
128            Some("toggled_by_variant") => {
129                toggled_by_variant = get_string_value_from_attribute(attr)
130            }
131            Some("variant_by") => variant_by = get_string_value_from_attribute(attr),
132            Some("length_by") => length_by = get_string_value_from_attribute(attr),
133            Some("bits") => bits_count = get_int_value_from_attribute(attr).map(|b| b as u8),
134            _ => {} // None => continue
135        }
136    }
137
138    let val_reference = if matches!(single_ident_type_name, Some(s) if s == String::from("RefCell"))
139    {
140        if read {
141            quote! {
142                *#field_ident.borrow()
143            }
144        } else {
145            quote! {
146                *_p_val.borrow()
147            }
148        }
149    } else {
150        if read {
151            quote! {
152                _p_val
153            }
154        } else {
155            quote! {
156                *_p_val
157            }
158        }
159    };
160
161    // Runtime toggle_key
162    let toggles = if let Some(key) = toggle_key {
163        quote! {
164            _p_config.set_toggle(#key, #val_reference);
165        }
166    } else {
167        quote! {}
168    };
169
170    // Runtime length_for keys (support multiple)
171    let length_calls: Vec<proc_macro2::TokenStream> = length_keys
172        .iter()
173        .map(|k| quote! { _p_config.set_length(#k, #val_reference as usize); })
174        .collect();
175
176    let length = if !length_calls.is_empty() {
177        quote! { #(#length_calls)* }
178    } else {
179        quote! {}
180    };
181
182    // Runtime variant_for keys (support multiple)
183    let variant_calls: Vec<proc_macro2::TokenStream> = variant_keys
184        .iter()
185        .map(|k| quote! { _p_config.set_variant(#k, #val_reference as u8); })
186        .collect();
187
188    let variant = if !variant_calls.is_empty() {
189        quote! { #(#variant_calls)* }
190    } else {
191        quote! {}
192    };
193
194    // Compose code to handle field
195    let f_ident = if is_enum {
196        quote! { #field_ident }
197    } else {
198        quote! { &self.#field_ident }
199    };
200
201    let before = if read {
202        quote! {}
203    } else {
204        quote! {
205            let _p_val = #f_ident;
206            #toggles
207            #length
208            #variant
209        }
210    };
211
212    let after = if read {
213        quote! {
214            let #field_ident = _p_val;
215            #toggles
216            #length
217            #variant
218        }
219    } else {
220        quote! {}
221    };
222
223    let handle_field = generate_code_for_handling_field(
224        read,
225        field_type,
226        field_ident,
227        bits_count,
228        toggled_by,
229        toggled_by_variant,
230        variant_by,
231        length_by,
232        is_dynamic_int,
233        has_dynamic_length,
234        key_dyn_length,
235        val_dyn_length,
236        multi_enum,
237        false,
238        0,
239    );
240
241    quote! {
242        #before
243        #handle_field
244        #after
245    }
246}
247
248fn generate_struct_serializer(
249    read: bool,
250    ast: &DeriveInput,
251    data_struct: &syn::DataStruct,
252) -> proc_macro::TokenStream {
253    let fields = &data_struct.fields;
254    let struct_name = &ast.ident;
255
256    // Iterate all fields in the struct
257    let field_serializations = fields.iter().map(|field| {
258        generate_field_serializer(
259            read,
260            &field
261                .ident
262                .as_ref()
263                .expect("binary-codec does not support fields without a name"),
264            &field.ty,
265            field,
266            false,
267        )
268    });
269
270    let error_type = generate_error_type(read, &ast.attrs);
271    let serializer_code = if read {
272        let vars = fields.iter().map(|f| f.ident.as_ref().unwrap());
273
274        // read bytes code
275        quote! {
276            impl<T : Clone> binary_codec::BinaryDeserializer<T, #error_type> for #struct_name {
277                fn read_bytes(
278                    stream: &mut binary_codec::BitStreamReader,
279                    config: Option<&mut binary_codec::SerializerConfig<T>>,
280                ) -> Result<Self, #error_type> {
281                    let mut _new_config = binary_codec::SerializerConfig::new(None);
282                    let _p_config = config.unwrap_or(&mut _new_config);
283                    let _p_stream = stream;
284
285                    #(#field_serializations)*
286
287                    Ok(Self {
288                        #(#vars),*
289                    })
290                }
291            }
292        }
293    } else {
294        // write bytes code
295        quote! {
296            impl<T : Clone> binary_codec::BinarySerializer<T, #error_type> for #struct_name {
297                fn write_bytes(
298                    &self,
299                    stream: &mut binary_codec::BitStreamWriter,
300                    config: Option<&mut binary_codec::SerializerConfig<T>>,
301                ) -> Result<(), #error_type> {
302                    let mut _new_config = binary_codec::SerializerConfig::new(None);
303                    let _p_config = config.unwrap_or(&mut _new_config);
304                    let _p_stream = stream;
305
306                    #(#field_serializations)*
307                    Ok(())
308                }
309            }
310        }
311    };
312
313    serializer_code.into()
314}
315
316fn generate_enum_serializer(
317    read: bool,
318    ast: &DeriveInput,
319    data_enum: &syn::DataEnum,
320) -> proc_macro::TokenStream {
321    let enum_name = &ast.ident;
322    let error_type = generate_error_type(read, &ast.attrs);
323
324    let mut no_disc_prefix = false;
325
326    // Search attributes for variant_by declarations
327    for attr in ast.attrs.iter() {
328        // #[no_disc_prefix] attribute
329        if attr.path().is_ident("no_discriminator") {
330            no_disc_prefix = true;
331        }
332    }
333
334    let mut configure_functions = Vec::new();
335
336    // Compute discriminant values following Rust rules: explicit values are used,
337    // unspecified values get previous + 1 (or 0 for the first unspecified).
338    let mut disc_values: Vec<u8> = Vec::with_capacity(data_enum.variants.len());
339    let mut last_val: Option<u8> = None;
340    for variant in data_enum.variants.iter() {
341        let val = if let Some((_, expr)) = &variant.discriminant {
342            match expr {
343                syn::Expr::Lit(syn::ExprLit {
344                    lit: Lit::Int(lit_int),
345                    ..
346                }) => lit_int
347                    .base10_parse::<u8>()
348                    .expect("Invalid discriminant integer"),
349                _ => panic!("Discriminant must be an integer literal"),
350            }
351        } else {
352            match last_val {
353                Some(v) => v + 1,
354                None => 0,
355            }
356        };
357
358        if val > u8::from(u8::MAX) {
359            panic!("Discriminant value too large (must fit in u8)");
360        }
361
362        disc_values.push(val);
363        last_val = Some(val);
364    }
365
366    // Create discriminant getter
367    let disc_variants = data_enum
368        .variants
369        .iter()
370        .enumerate()
371        .map(|(i, variant)| {
372            let var_ident = &variant.ident;
373            let disc_value = disc_values[i];
374
375            for attr in variant.attrs.iter() {
376                if attr.path().is_ident("toggled_by") {
377                    let field = get_string_value_from_attribute(attr)
378                        .expect("toggled_by for multi_enum should have a value");
379                    configure_functions.push(quote! {
380                        _p_config.configure_multi_disc(stringify!(#enum_name), #disc_value, #field);
381                    });
382                }
383            }
384
385            match &variant.fields {
386                Fields::Unit => quote! {
387                    Self::#var_ident => #disc_value
388                },
389                Fields::Unnamed(_) => quote! {
390                    Self::#var_ident(..) => #disc_value
391                },
392                Fields::Named(_) => quote! {
393                    Self::#var_ident { .. } => #disc_value
394                },
395            }
396        })
397        .collect::<Vec<_>>();
398
399    // Assign discriminant values starting from 0
400    let serialization_variants = data_enum.variants.iter().enumerate().map(|(i, variant)| {
401        let var_ident = &variant.ident;
402        let disc_value = disc_values[i];
403        let fields = &variant.fields;
404
405        // TODO: problem might be that attrs are not used from the fields??.
406
407        let write_disc = if no_disc_prefix {
408            quote! {}
409        } else {
410            quote! {
411                let _p_disc: u8 = #disc_value;
412                _p_stream.write_fixed_int(_p_disc);
413            }
414        };
415
416        match fields {
417            Fields::Unit => {
418                if read {
419                    quote! {
420                        #disc_value => {
421                            Ok(Self::#var_ident)
422                        }
423                    }
424                } else {
425                    quote! {
426                        Self::#var_ident => {
427                            #write_disc
428                        }
429                    }
430                }
431            }
432            Fields::Unnamed(fields_unnamed) => {
433                let field_count = fields_unnamed.unnamed.len();
434                let idents: Vec<_> = (0..field_count).map(|i| format_ident!("f{}", i)).collect();
435                let ident_refs: Vec<&syn::Ident> = idents.iter().collect();
436                let field_serializations =
437                    generate_enum_field_serializations(read, &ident_refs, &fields_unnamed.unnamed);
438                if read {
439                    quote! {
440                        #disc_value => {
441                            #(#field_serializations)*
442                            Ok(Self::#var_ident(#(#idents),*))
443                        }
444                    }
445                } else {
446                    quote! {
447                        Self::#var_ident(#(#idents),*) => {
448                            #write_disc
449                            #(#field_serializations)*
450                        }
451                    }
452                }
453            }
454            Fields::Named(fields_named) => {
455                let field_idents: Vec<_> = fields_named
456                    .named
457                    .iter()
458                    .map(|f| f.ident.as_ref().unwrap())
459                    .collect();
460
461                let field_serializations =
462                    generate_enum_field_serializations(read, &field_idents, &fields_named.named);
463
464                if read {
465                    quote! {
466                        #disc_value => {
467                            #(#field_serializations)*
468                            Ok(Self::#var_ident { #(#field_idents),* })
469                        }
470                    }
471                } else {
472                    quote! {
473                        Self::#var_ident { #(#field_idents),* } => {
474                            #write_disc
475                            #(#field_serializations)*
476                        }
477                    }
478                }
479            }
480        }
481    });
482
483    if read {
484        quote! {
485            impl #enum_name {
486                pub fn configure_multi_disc<T : Clone>(config: &mut binary_codec::SerializerConfig<T>) {
487                    let _p_config = config;
488                    #(#configure_functions)*
489                }
490            }
491
492            impl<T : Clone> binary_codec::BinaryDeserializer<T, #error_type> for #enum_name {
493                fn read_bytes(
494                    stream: &mut binary_codec::BitStreamReader,
495                    config: Option<&mut binary_codec::SerializerConfig<T>>,
496                ) -> Result<Self, #error_type> {
497                    let mut _new_config = binary_codec::SerializerConfig::new(None);
498                    let _p_config = config.unwrap_or(&mut _new_config);
499                    let _p_stream = stream;
500
501                    let _p_disc = if let Some(disc) = _p_config.discriminator.take() {
502                        disc
503                    } else {
504                        _p_stream.read_fixed_int()?
505                    };
506
507                    match _p_disc {
508                        #(#serialization_variants,)*
509                        _ => Err(binary_codec::DeserializationError::UnknownDiscriminant(_p_disc).into()),
510                    }
511                }
512            }
513        }
514        .into()
515    } else {
516        quote! {
517            impl<T : Clone> binary_codec::BinarySerializer<T, #error_type> for #enum_name {
518                fn write_bytes(
519                    &self,
520                    stream: &mut binary_codec::BitStreamWriter,
521                    config: Option<&mut binary_codec::SerializerConfig<T>>,
522                ) -> Result<(), #error_type> {
523                    let mut _new_config = binary_codec::SerializerConfig::new(None);
524                    let _p_config = config.unwrap_or(&mut _new_config);
525                    #(#configure_functions)*
526                    let _p_stream = stream;
527
528                    match self {
529                        #(#serialization_variants)*
530                    }
531
532                    Ok(())
533                }
534            }
535
536            impl #enum_name {
537                pub fn get_discriminator(&self) -> u8 {
538                    match self {
539                        #(#disc_variants,)*
540                    }
541                }
542            }
543        }
544        .into()
545    }
546}
547
548fn generate_enum_field_serializations(
549    read: bool,
550    idents: &Vec<&syn::Ident>,
551    fields: &Punctuated<syn::Field, Comma>,
552) -> Vec<proc_macro2::TokenStream> {
553    let field_serializations = fields.iter().enumerate().map(|(i, f)| {
554        let field_type = &f.ty;
555        let field_ident = &idents[i];
556
557        generate_field_serializer(read, &field_ident, field_type, f, true)
558    });
559    field_serializations.collect()
560}
561
562fn generate_code_for_handling_field(
563    read: bool,
564    field_type: &Type,
565    field_name: &syn::Ident,
566    bits_count: Option<u8>,
567    toggled_by: Option<String>,
568    toggled_by_variant: Option<String>,
569    variant_by: Option<String>,
570    length_by: Option<String>,
571    is_dynamic_int: bool,
572    has_dynamic_length: bool,
573    key_dyn_length: bool,
574    val_dyn_length: bool,
575    multi_enum: bool,
576    direct_collection_child: bool,
577    level: usize,
578) -> proc_macro2::TokenStream {
579    if let Type::Path(path) = field_type {
580        let path = &path.path;
581
582        if let Some(ident) = path.get_ident() {
583            let ident_name = ident.to_string();
584
585            // Single segment without arguments
586            match ident_name.as_str() {
587                "bool" => {
588                    if read {
589                        quote! { let _p_val = _p_stream.read_bit()?;}
590                    } else {
591                        quote! { _p_stream.write_bit(*_p_val); }
592                    }
593                }
594                "i8" => {
595                    if let Some(bits_count) = bits_count.as_ref() {
596                        if *bits_count < 1 || *bits_count > 7 {
597                            panic!("Bits count should be between 1 and 7");
598                        }
599
600                        if read {
601                            quote! { let _p_val = binary_codec::ZigZag::to_signed(_p_stream.read_small(#bits_count)?); }
602                        } else {
603                            quote! { _p_stream.write_small(binary_codec::ZigZag::to_unsigned(*_p_val), #bits_count); }
604                        }
605                    } else {
606                        if read {
607                            quote! { let _p_val = _p_stream.read_fixed_int()?; }
608                        } else {
609                            quote! { _p_stream.write_fixed_int(*_p_val); }
610                        }
611                    }
612                }
613                "u8" => {
614                    if let Some(bits_count) = bits_count.as_ref() {
615                        if *bits_count < 1 || *bits_count > 7 {
616                            panic!("Bits count should be between 1 and 7");
617                        }
618
619                        if read {
620                            quote! { let _p_val = _p_stream.read_small(#bits_count)?; }
621                        } else {
622                            quote! { _p_stream.write_small(*_p_val, #bits_count); }
623                        }
624                    } else {
625                        if read {
626                            quote! { let _p_val = _p_stream.read_byte()?; }
627                        } else {
628                            quote! { _p_stream.write_byte(*_p_val); }
629                        }
630                    }
631                }
632                "u16" | "u32" | "u64" | "u128" => {
633                    if is_dynamic_int {
634                        let dynint: proc_macro2::TokenStream = generate_dynint(read);
635                        if read {
636                            quote! {
637                                #dynint
638                                let _p_val = _p_dyn as #ident;
639                            }
640                        } else {
641                            quote! {
642                                let _p_dyn = *_p_val as u128;
643                                #dynint
644                            }
645                        }
646                    } else {
647                        if read {
648                            quote! { let _p_val = _p_stream.read_fixed_int()?; }
649                        } else {
650                            quote! { _p_stream.write_fixed_int(*_p_val); }
651                        }
652                    }
653                }
654                "i16" | "i32" | "i64" | "i128" => {
655                    if is_dynamic_int {
656                        let dynint: proc_macro2::TokenStream = generate_dynint(read);
657                        if read {
658                            quote! {
659                                #dynint
660                                let _p_val: #ident = binary_codec::ZigZag::to_signed(_p_dyn);
661                            }
662                        } else {
663                            quote! {
664                                let _p_dyn = binary_codec::ZigZag::to_unsigned(*_p_val) as u128;
665                                #dynint
666                            }
667                        }
668                    } else {
669                        if read {
670                            quote! { let _p_val = _p_stream.read_fixed_int()?; }
671                        } else {
672                            quote! { _p_stream.write_fixed_int(*_p_val); }
673                        }
674                    }
675                }
676                "f32" | "f64" => {
677                    if read {
678                        quote! { let _p_val = _p_stream.read_fixed_int()?; }
679                    } else {
680                        quote! { _p_stream.write_fixed_int(*_p_val); }
681                    }
682                }
683                "String" => {
684                    let size_key = generate_size_key(length_by, has_dynamic_length).1;
685
686                    if read {
687                        quote! {
688                            let _p_val = binary_codec::utils::read_string(_p_stream, #size_key, _p_config)?;
689                        }
690                    } else {
691                        quote! {
692                            binary_codec::utils::write_string(_p_val, #size_key, _p_stream, _p_config)?;
693                        }
694                    }
695                }
696                _ => {
697                    let size_key = generate_size_key(length_by, has_dynamic_length).1;
698
699                    let variant_code = if variant_by.is_some() {
700                        quote! {
701                            _p_config.discriminator = _p_config.get_variant(#variant_by);
702                        }
703                    } else if multi_enum {
704                        let config_multi = if !direct_collection_child {
705                            quote! { #ident::configure_multi_disc(_p_config); }
706                        } else {
707                            quote! {}
708                        };
709
710                        quote! {
711                            #config_multi
712                            _p_config.discriminator = _p_config.get_next_multi_disc(stringify!(#field_name), #ident_name);
713                        }
714                    } else {
715                        quote! {
716                            _p_config.discriminator = None;
717                        }
718                    };
719
720                    if read {
721                        quote! {
722                            #variant_code
723                            let _p_val = binary_codec::utils::read_object(_p_stream, #size_key, _p_config)?;
724                        }
725                    } else {
726                        quote! {
727                            #variant_code
728                            binary_codec::utils::write_object(_p_val, #size_key, _p_stream, _p_config)?;
729                        }
730                    }
731                }
732            }
733        } else {
734            // Multiple segments, or arguments
735            if path.segments.len() == 1 {
736                let ident = &path.segments[0].ident;
737                let ident_name = ident.to_string();
738
739                match ident_name.as_ref() {
740                    "RefCell" => {
741                        let inner_type = get_inner_type(path).expect("Option missing inner type");
742                        let handle = generate_code_for_handling_field(
743                            read,
744                            inner_type,
745                            field_name,
746                            bits_count,
747                            None,
748                            None,
749                            variant_by,
750                            length_by,
751                            is_dynamic_int,
752                            has_dynamic_length,
753                            key_dyn_length,
754                            val_dyn_length,
755                            multi_enum,
756                            false,
757                            level + 1,
758                        );
759
760                        if read {
761                            quote! {
762                                #handle
763                                let _p_val = RefCell::new(_p_val);
764                            }
765                        } else {
766                            quote! {
767                                let _p_val = &*_p_val.borrow();
768                                #handle
769                            }
770                        }
771                    }
772                    "Option" => {
773                        let inner_type = get_inner_type(path).expect("Option missing inner type");
774                        let handle = generate_code_for_handling_field(
775                            read,
776                            inner_type,
777                            field_name,
778                            bits_count,
779                            None,
780                            None,
781                            variant_by,
782                            length_by,
783                            is_dynamic_int,
784                            has_dynamic_length,
785                            key_dyn_length,
786                            val_dyn_length,
787                            multi_enum,
788                            false,
789                            level + 1,
790                        );
791
792                        let option_name: syn::Ident = format_ident!("__option_{}", level);
793
794                        if let Some(toggled_by) = toggled_by {
795                            // If toggled_by is set, read or write it
796                            let toggled_by = quote! {
797                                _p_config.get_toggle(#toggled_by).unwrap_or(false)
798                            };
799
800                            if read {
801                                quote! {
802                                    let mut #option_name: Option<#inner_type> = None;
803                                    if #toggled_by {
804                                        #handle
805                                        #option_name = Some(_p_val);
806                                    }
807                                    let _p_val = #option_name;
808                                }
809                            } else {
810                                quote! {
811                                    if #toggled_by {
812                                        let _p_val = _p_val.as_ref().expect("Expected Some value, because toggled_by field is true");
813                                        #handle
814                                    }
815                                }
816                            }
817                        } else if let Some(toggled_by_variant) = toggled_by_variant {
818                            // If toggled_by_variant is set, read or write it
819                            let toggled_by = quote! {
820                                _p_config.get_variant_toggle(#toggled_by_variant).unwrap_or(false)
821                            };
822
823                            if read {
824                                quote! {
825                                    let mut #option_name: Option<#inner_type> = None;
826                                    if #toggled_by {
827                                        #handle
828                                        #option_name = Some(_p_val);
829                                    }
830                                    let _p_val = #option_name;
831                                }
832                            } else {
833                                quote! {
834                                    if #toggled_by {
835                                        let _p_val = _p_val.as_ref().expect("Expected Some value, because toggled_by_variant field evalutates to true");
836                                        #handle
837                                    }
838                                }
839                            }
840                        } else {
841                            // If space available, read it, write it if not None
842                            if read {
843                                quote! {
844                                    let mut #option_name: Option<#inner_type> = None;
845                                    if _p_stream.bytes_left() > 0 {
846                                        #handle
847                                        #option_name = Some(_p_val);
848                                    }
849                                    let _p_val = #option_name;
850                                }
851                            } else {
852                                quote! {
853                                    if let Some(_p_val) = _p_val.as_ref() {
854                                        #handle
855                                    }
856                                }
857                            }
858                        }
859                    }
860                    "Vec" => {
861                        let vec_name = format_ident!("__val_{}", level);
862                        let inner_type = get_inner_type(path).expect("Vec missing inner type");
863
864                        // If inner type is u8, optimize to bulk read/write bytes
865                        if let Type::Path(inner_path) = inner_type {
866                            if let Some(inner_ident) = inner_path.path.get_ident() {
867                                if inner_ident == "u8" {
868                                    let (has_size, size_key) =
869                                        generate_size_key(length_by, has_dynamic_length);
870
871                                    if read {
872                                        if has_size || multi_enum {
873                                            // sized read
874                                            let len_code = if multi_enum {
875                                                quote! {
876                                                    // multi_enum sized Vec<u8>
877                                                    let _p_len = _p_config.get_multi_disc_size("u8");
878                                                }
879                                            } else {
880                                                quote! {
881                                                    let _p_len = binary_codec::utils::get_read_size(_p_stream, #size_key, _p_config)?;
882                                                }
883                                            };
884
885                                            return quote! {
886                                                #len_code
887                                                let _p_val = _p_stream.read_bytes(_p_len)?.to_vec();
888                                            };
889                                        } else {
890                                            // read all remaining bytes
891                                            return quote! {
892                                                let _p_len = _p_stream.bytes_left();
893                                                let _p_val = _p_stream.read_bytes(_p_len)?.to_vec();
894                                            };
895                                        }
896                                    } else {
897                                        // write path: if sized, write size first
898                                        let write_size = if has_size {
899                                            quote! {
900                                                let _p_len = _p_val.len();
901                                                binary_codec::utils::write_size(_p_len, #size_key, _p_stream, _p_config)?;
902                                            }
903                                        } else {
904                                            quote! {}
905                                        };
906
907                                        return quote! {
908                                            #write_size
909                                            _p_stream.write_bytes(_p_val);
910                                        };
911                                    }
912                                }
913                            }
914                        }
915
916                        // Fallback to element-wise handling for non-u8 inner types
917                        let handle = generate_code_for_handling_field(
918                            read,
919                            inner_type,
920                            field_name,
921                            bits_count,
922                            None,
923                            None,
924                            None,
925                            None,
926                            is_dynamic_int,
927                            val_dyn_length,
928                            false,
929                            false,
930                            multi_enum,
931                            true,
932                            level + 1,
933                        );
934
935                        let (has_size, size_key) = generate_size_key(length_by, has_dynamic_length);
936
937                        let write_code = quote! {
938                            for _p_val in _p_val {
939                                #handle
940                            }
941                        };
942
943                        if has_size || (read && multi_enum) {
944                            if read {
945                                let len_code = if multi_enum && let Type::Path(path) = inner_type {
946                                    let enum_ident = path
947                                        .path
948                                        .get_ident()
949                                        .expect("Expected ident for multi_enum inner type");
950                                    quote! {
951                                        #enum_ident::configure_multi_disc(_p_config);
952                                        let _p_len = _p_config.get_multi_disc_size(stringify!(#enum_ident));
953                                    }
954                                } else {
955                                    quote! {
956                                        let _p_len = binary_codec::utils::get_read_size(_p_stream, #size_key, _p_config)?;
957                                    }
958                                };
959
960                                quote! {
961                                    #len_code
962                                    let mut #vec_name = Vec::<#inner_type>::with_capacity(_p_len);
963                                    for _ in 0.._p_len {
964                                        #handle
965                                        #vec_name.push(_p_val);
966                                    }
967                                    let _p_val = #vec_name;
968                                }
969                            } else {
970                                quote! {
971                                    let _p_len = _p_val.len();
972                                    binary_codec::utils::write_size(_p_len, #size_key, _p_stream, _p_config)?;
973                                    #write_code
974                                }
975                            }
976                        } else {
977                            if read {
978                                quote! {
979                                    let mut #vec_name = Vec::<#inner_type>::new();
980                                    while _p_stream.bytes_left() > 0 {
981                                        #handle
982                                        #vec_name.push(_p_val);
983                                    }
984                                    let _p_val = #vec_name;
985                                }
986                            } else {
987                                quote! {
988                                    #write_code
989                                }
990                            }
991                        }
992                    }
993                    "HashMap" => {
994                        let (key_type, value_type) =
995                            get_two_types(path).expect("Failed to get HashMap types");
996
997                        let handle_key = generate_code_for_handling_field(
998                            read,
999                            key_type,
1000                            field_name,
1001                            None,
1002                            None,
1003                            None,
1004                            None,
1005                            None,
1006                            is_dynamic_int,
1007                            key_dyn_length,
1008                            false,
1009                            false,
1010                            false,
1011                            false,
1012                            level + 1,
1013                        );
1014
1015                        let handle_value = generate_code_for_handling_field(
1016                            read,
1017                            value_type,
1018                            field_name,
1019                            None,
1020                            None,
1021                            None,
1022                            None,
1023                            None,
1024                            is_dynamic_int,
1025                            val_dyn_length,
1026                            false,
1027                            false,
1028                            false,
1029                            false,
1030                            level + 1,
1031                        );
1032
1033                        let (has_size, size_key) = generate_size_key(length_by, has_dynamic_length);
1034
1035                        let write_code = quote! {
1036                            for (key, value) in _p_val {
1037                                let _p_val = key;
1038                                #handle_key
1039                                let _p_val = value;
1040                                #handle_value
1041                            }
1042                        };
1043
1044                        if read {
1045                            if has_size {
1046                                quote! {
1047                                    let _p_len = binary_codec::utils::get_read_size(_p_stream, #size_key, _p_config)?;
1048                                    let mut _p_map = std::collections::HashMap::<#key_type, #value_type>::with_capacity(_p_len);
1049                                    for _ in 0.._p_len {
1050                                        let _p_key;
1051                                        #handle_key
1052                                        _p_key = _p_val;
1053                                        let _p_value;
1054                                        #handle_value
1055                                        _p_value = _p_val;
1056                                        _p_map.insert(_p_key, _p_value);
1057                                    }
1058                                    let _p_val = _p_map;
1059                                }
1060                            } else {
1061                                quote! {
1062                                    let mut _p_map = std::collections::HashMap::<#key_type, #value_type>::new();
1063                                    while _p_stream.bytes_left() > 0 {
1064                                        let _p_key;
1065                                        #handle_key
1066                                        _p_key = _p_val;
1067                                        let _p_value;
1068                                        #handle_value
1069                                        _p_value = _p_val;
1070                                        _p_map.insert(_p_key, _p_value);
1071                                    }
1072                                    let _p_val = _p_map;
1073                                }
1074                            }
1075                        } else {
1076                            if has_size {
1077                                quote! {
1078                                    let _p_len = _p_val.len();
1079                                    binary_codec::utils::write_size(_p_len, #size_key, _p_stream, _p_config)?;
1080                                    #write_code
1081                                }
1082                            } else {
1083                                quote! {
1084                                    #write_code
1085                                }
1086                            }
1087                        }
1088                    }
1089                    _ => {
1090                        panic!("Type not implemented")
1091                    }
1092                }
1093            } else {
1094                panic!("Multi-segment paths are not supported");
1095            }
1096        }
1097    } else if let Type::Array(array) = field_type {
1098        let len: usize = if let syn::Expr::Lit(ref arr_len_lit) = array.len {
1099            if let Lit::Int(ref lit_int) = arr_len_lit.lit {
1100                lit_int
1101                    .base10_parse()
1102                    .expect("Failed to parse literal to usize")
1103            } else {
1104                panic!("Expected an int to determine array length");
1105            }
1106        } else {
1107            panic!("Expected literal to determine array length");
1108        };
1109
1110        let array_type = &*array.elem;
1111        // Optimize [u8; N] to bulk read_bytes / write_bytes
1112        if let Type::Path(at_path) = array_type {
1113            if let Some(at_ident) = at_path.path.get_ident() {
1114                if at_ident == "u8" {
1115                    if read {
1116                        quote! {
1117                            let _p_slice = _p_stream.read_bytes(#len)?;
1118                            let _p_val = <[u8; #len]>::try_from(_p_slice).expect("Failed to convert slice to array");
1119                        }
1120                    } else {
1121                        quote! {
1122                            _p_stream.write_bytes(_p_val);
1123                        }
1124                    }
1125                } else {
1126                    let handle = generate_code_for_handling_field(
1127                        read,
1128                        array_type,
1129                        field_name,
1130                        bits_count,
1131                        None,
1132                        None,
1133                        None,
1134                        None,
1135                        is_dynamic_int,
1136                        val_dyn_length,
1137                        false,
1138                        false,
1139                        false,
1140                        true,
1141                        level + 1,
1142                    );
1143
1144                    let array_name = format_ident!("__val_{}", level);
1145
1146                    if read {
1147                        quote! {
1148                            let mut #array_name = Vec::<#array_type>::with_capacity(#len);
1149                            for _ in 0..#len {
1150                                #handle;
1151                                #array_name.push(_p_val);
1152                            }
1153                            let _p_val = TryInto::<[#array_type; #len]>::try_into(#array_name).expect("Failed to convert Vec to array");
1154                        }
1155                    } else {
1156                        quote! {
1157                            for _p_val in _p_val {
1158                                #handle
1159                            }
1160                        }
1161                    }
1162                }
1163            } else {
1164                // fallback to element handling
1165                let handle = generate_code_for_handling_field(
1166                    read,
1167                    array_type,
1168                    field_name,
1169                    bits_count,
1170                    None,
1171                    None,
1172                    None,
1173                    None,
1174                    is_dynamic_int,
1175                    val_dyn_length,
1176                    false,
1177                    false,
1178                    false,
1179                    true,
1180                    level + 1,
1181                );
1182
1183                let array_name = format_ident!("__val_{}", level);
1184
1185                if read {
1186                    quote! {
1187                        let mut #array_name = Vec::<#array_type>::with_capacity(#len);
1188                        for _ in 0..#len {
1189                            #handle;
1190                            #array_name.push(_p_val);
1191                        }
1192                        let _p_val = TryInto::<[#array_type; #len]>::try_into(#array_name).expect("Failed to convert Vec to array");
1193                    }
1194                } else {
1195                    quote! {
1196                        for _p_val in _p_val {
1197                            #handle
1198                        }
1199                    }
1200                }
1201            }
1202        } else {
1203            panic!("Unsupported array element type");
1204        }
1205    } else {
1206        panic!("Field type of '{:?}' not supported", field_name);
1207    }
1208}
1209
1210fn generate_error_type(read: bool, attrs: &[Attribute]) -> proc_macro2::TokenStream {
1211    if let Some(custom) = get_custom_error_type(read, attrs) {
1212        return custom;
1213    }
1214
1215    if read {
1216        quote! { binary_codec::DeserializationError }
1217    } else {
1218        quote! { binary_codec::SerializationError }
1219    }
1220}
1221
1222fn get_custom_error_type(read: bool, attrs: &[Attribute]) -> Option<proc_macro2::TokenStream> {
1223    let specific = if read { "codec_de_error" } else { "codec_ser_error" };
1224
1225    let specific_value = attrs
1226        .iter()
1227        .find(|attr| attr.path().is_ident(specific))
1228        .and_then(get_string_value_from_attribute);
1229
1230    if let Some(value) = specific_value {
1231        return Some(parse_error_type(&value));
1232    }
1233
1234    let common_value = attrs
1235        .iter()
1236        .find(|attr| attr.path().is_ident("codec_error"))
1237        .and_then(get_string_value_from_attribute);
1238
1239    common_value.map(|value| parse_error_type(&value))
1240}
1241
1242fn parse_error_type(value: &str) -> proc_macro2::TokenStream {
1243    let ty: Type = syn::parse_str(value).expect("Invalid error type for codec_error");
1244    quote! { #ty }
1245}
1246
1247fn generate_size_key(
1248    length_by: Option<String>,
1249    has_dynamic_length: bool,
1250) -> (bool, proc_macro2::TokenStream) {
1251    if let Some(length_by) = length_by.as_ref() {
1252        (true, quote! { Some(#length_by) })
1253    } else if has_dynamic_length {
1254        (true, quote! { Some("__dynamic") })
1255    } else {
1256        (false, quote! { None })
1257    }
1258}
1259
1260fn get_string_value_from_attribute(attr: &Attribute) -> Option<String> {
1261    match &attr.meta {
1262        syn::Meta::Path(_) => None,
1263        syn::Meta::List(list_value) => {
1264            // #[myattribute("value")]
1265            for token in list_value.tokens.clone().into_iter() {
1266                if let proc_macro2::TokenTree::Literal(lit) = token {
1267                    return Some(lit.to_string().trim_matches('"').to_string());
1268                }
1269            }
1270
1271            None
1272        }
1273        syn::Meta::NameValue(name_value) => {
1274            if let syn::Expr::Lit(lit_expr) = &name_value.value {
1275                if let Lit::Str(lit_str) = &lit_expr.lit {
1276                    return Some(lit_str.value());
1277                }
1278            }
1279
1280            None
1281        }
1282    }
1283}
1284
1285fn get_int_value_from_attribute(attr: &Attribute) -> Option<i32> {
1286    match &attr.meta {
1287        syn::Meta::Path(_) => None,
1288        syn::Meta::List(list_value) => {
1289            // #[myattribute(value)]
1290            for token in list_value.tokens.clone().into_iter() {
1291                if let proc_macro2::TokenTree::Literal(lit) = token {
1292                    if let Ok(val) = lit.to_string().parse::<i32>() {
1293                        return Some(val);
1294                    }
1295                }
1296            }
1297
1298            None
1299        }
1300        syn::Meta::NameValue(name_value) => {
1301            if let syn::Expr::Lit(lit_expr) = &name_value.value {
1302                if let Lit::Int(lit_int) = &lit_expr.lit {
1303                    return Some(lit_int.base10_parse().expect("Not a valid int value"));
1304                }
1305            }
1306
1307            None
1308        }
1309    }
1310}
1311
1312fn get_inner_type(path: &syn::Path) -> Option<&syn::Type> {
1313    if let Some(PathArguments::AngleBracketed(args)) =
1314        path.segments.last().map(|seg| &seg.arguments)
1315    {
1316        if let Some(arg) = args.args.first() {
1317            if let syn::GenericArgument::Type(inner_type) = arg {
1318                return Some(inner_type);
1319            }
1320        }
1321    }
1322
1323    None
1324}
1325
1326fn get_two_types(path: &syn::Path) -> Option<(&syn::Type, &syn::Type)> {
1327    if let Some(PathArguments::AngleBracketed(args)) =
1328        path.segments.last().map(|seg| &seg.arguments)
1329    {
1330        let mut types = args.args.iter().filter_map(|arg| {
1331            if let syn::GenericArgument::Type(inner_type) = arg {
1332                Some(inner_type)
1333            } else {
1334                None
1335            }
1336        });
1337
1338        if let (Some(t1), Some(t2)) = (types.next(), types.next()) {
1339            return Some((t1, t2));
1340        }
1341    }
1342
1343    None
1344}
1345
1346fn generate_dynint(read: bool) -> proc_macro2::TokenStream {
1347    if read {
1348        quote! {
1349            let _p_dyn = _p_stream.read_dyn_int()?;
1350        }
1351    } else {
1352        quote! {
1353            _p_stream.write_dyn_int(_p_dyn);
1354        }
1355    }
1356}