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