wopt/
lib.rs

1#![allow(unused_variables)]
2
3use proc_macro::{Span, TokenStream};
4use quote::quote;
5use syn::{
6    DeriveInput, Field, Fields, Ident, Index, LitStr, Meta, Path, PathSegment, Type, TypePath,
7    parse_macro_input, punctuated::Iter,
8};
9
10#[cfg(any(feature = "bf", feature = "bytemuck"))]
11use syn::{Expr, Lit};
12
13#[cfg(feature = "bytemuck")]
14use std::{
15    fs::File,
16    io::{Read, Seek, SeekFrom, Write},
17};
18
19#[cfg(all(not(feature = "bytemuck"), feature = "unchecked"))]
20compile_error!("Feature `unchecked` requires feature `bytemuck`.");
21
22#[cfg(feature = "bytemuck")]
23fn setup_counter_file() -> File {
24    let path = std::path::Path::new("target")
25        .join("tmp")
26        .join("wopt")
27        .join("counter");
28
29    // ensure directory exists
30    std::fs::create_dir_all(unsafe {
31        // SAFETY - impossible to be root/prefix and isn't empty.
32        path.parent().unwrap_unchecked()
33    })
34    .unwrap_or_else(|_| panic!("Failed to create {:?} directory.", path.parent()));
35
36    #[allow(clippy::suspicious_open_options)]
37    let mut f = File::options()
38        .read(true)
39        .write(true)
40        .create(true)
41        .open(&path)
42        .unwrap_or_else(|_| panic!("Failed to open {path:?}."));
43
44    let mut s = String::new();
45    f.read_to_string(&mut s).unwrap();
46    if s.trim().is_empty() {
47        f.write_all(b"0")
48            .unwrap_or_else(|_| panic!("Failed to write to {path:?} file."));
49        f.flush().unwrap_or_default();
50    }
51    f
52}
53
54#[cfg(feature = "bytemuck")]
55fn next_id(f: &mut File) -> u8 {
56    f.seek(SeekFrom::Start(0)).unwrap();
57
58    let mut s = String::new();
59    f.read_to_string(&mut s).unwrap();
60
61    let current = s.trim().parse::<u8>().unwrap();
62
63    let next: u8 = current.wrapping_add(1);
64
65    // overwrite with new value
66    f.set_len(0).unwrap();
67    f.seek(SeekFrom::Start(0)).unwrap();
68    f.write_all(next.to_string().as_bytes()).unwrap();
69    f.flush().unwrap();
70
71    current
72}
73
74fn get_opt_type(original: &Type) -> Type {
75    if let Type::Path(TypePath { path, .. }) = original
76        && let Some(last_segment) = path.segments.last()
77    {
78        let orig_ident = &last_segment.ident;
79        let new_ident = Ident::new(
80            format!("{orig_ident}Opt").as_str(),
81            Span::call_site().into(),
82        );
83
84        // Construct a new path with the modified ident and same arguments
85        let new_segment = PathSegment {
86            ident: new_ident,
87            arguments: last_segment.arguments.clone(),
88        };
89
90        let mut new_path = path.clone();
91        new_path.segments.pop();
92        new_path.segments.push(new_segment);
93
94        return Type::Path(TypePath {
95            qself: None,
96            path: new_path,
97        });
98    }
99    panic!("Unexpected syn::Type variant.")
100}
101
102struct FieldAttr<'a> {
103    field_name_opt: Option<&'a Option<Ident>>,
104    field_type: &'a Type,
105    field_type_opt: Type,
106    is_vec: bool,
107    is_optional: bool,
108    is_required: bool,
109    is_skipped: bool,
110    _is_serde: bool,
111    _serde_fn: Option<[Path; 2]>,
112}
113
114struct FieldAttrs<'a> {
115    attrs: Vec<FieldAttr<'a>>,
116    is_const: bool,
117}
118
119fn get_field_kvs(fields: Iter<Field>, is_named: bool) -> FieldAttrs {
120    let mut is_const = true;
121
122    let attrs = fields
123        .map(|field: &Field| {
124            let (mut is_vec, mut is_optional, mut is_required, mut is_skipped, mut _is_serde) =
125                Default::default();
126            let (mut ser, mut de) = Default::default();
127
128            // check for non-constant field (e.g., 'Vec')
129            if let Type::Path(path) = &field.ty
130                && path.path.segments.last().unwrap().ident == "Vec"
131            {
132                is_vec = true;
133                is_const = false;
134            }
135
136            if let Some(attr) = field.attrs.first()
137                && attr.path().is_ident("wopt")
138            {
139                attr.parse_nested_meta(|a| {
140                    if let Some(ident) = a.path.get_ident() {
141                        match ident.to_string().as_str() {
142                            "non_const" => is_const = false,
143                            "optional" => is_optional = true,
144                            "required" => is_required = true,
145                            "skip" => is_skipped = true,
146                            "serde" => _is_serde = true,
147                            "ser" => {
148                                let value = a.value()?;
149                                let s: LitStr = value.parse()?;
150                                let p = syn::parse_str::<Path>(s.value().as_str())?;
151                                ser = Some(p)
152                            }
153                            "de" => {
154                                let value = a.value()?;
155                                let s: LitStr = value.parse()?;
156                                let p = syn::parse_str::<Path>(s.value().as_str())?;
157                                de = Some(p)
158                            }
159                            attr => panic!("Unsupported attribute ({attr})."),
160                        }
161                    }
162                    Ok(())
163                })
164                .unwrap();
165
166                if is_required && is_skipped {
167                    panic!("`required` and `skip` can't be specified together.")
168                }
169            }
170
171            // determine if optional struct provided
172            let field_type = &field.ty;
173            let field_type_opt = if is_optional {
174                get_opt_type(field_type)
175            } else {
176                field_type.clone()
177            };
178
179            // override if any user-provided definitions
180            let _serde_fn = match (ser, de) {
181                (None, None) => None,
182                (Some(ser), Some(de)) => Some([ser, de]),
183                _ => panic!("Both ser/de need to be implemented."),
184            };
185
186            FieldAttr {
187                field_name_opt: is_named.then_some(&field.ident),
188                field_type,
189                field_type_opt,
190                is_vec,
191                is_optional,
192                is_required,
193                is_skipped,
194                _is_serde,
195                _serde_fn,
196            }
197        })
198        .collect::<Vec<_>>();
199    FieldAttrs { attrs, is_const }
200}
201
202#[proc_macro_derive(WithOpt, attributes(id, wopt))]
203pub fn wopt_derive(input: TokenStream) -> TokenStream {
204    // parse the input tokens into a syntax tree
205    let input = parse_macro_input!(input as DeriveInput);
206
207    // get the struct name
208    let name = &input.ident;
209
210    // identity of this optional struct
211    #[cfg(feature = "bytemuck")]
212    let mut id = None;
213
214    #[allow(unused_mut)]
215    let mut is_unit = false;
216
217    // the type of struct
218    let mut is_named = false;
219
220    // match on the fields of the struct
221    let info = if let syn::Data::Struct(ref data) = input.data {
222        match &data.fields {
223            Fields::Named(fields) => {
224                is_named = true;
225                get_field_kvs(fields.named.iter(), true)
226            }
227            Fields::Unnamed(fields) => get_field_kvs(fields.unnamed.iter(), false),
228            _ => {
229                #[cfg(not(feature = "bytemuck"))]
230                panic!("Unit structs are only supported with the `bytemuck` feature.");
231
232                #[cfg(feature = "bytemuck")]
233                {
234                    is_unit = true;
235                    FieldAttrs {
236                        attrs: Vec::new(),
237                        is_const: true,
238                    }
239                }
240            }
241        }
242    } else {
243        panic!("Only structs are supported");
244    };
245
246    if info.attrs.is_empty() && !is_unit {
247        panic!("Must have at least 1 field.")
248    }
249
250    let mut derives = Vec::new();
251    let mut _no_serde = false;
252
253    // process any `#[wopt(...)]` attributes
254    for attr in &input.attrs {
255        if attr.path().is_ident("wopt") {
256            let meta = attr.parse_args::<Meta>().unwrap();
257
258            match &meta {
259                Meta::Path(path) => {
260                    if !path.is_ident("no_serde") {
261                        panic!("Only 'no_serde' path meta is supported.")
262                    }
263                    _no_serde = true
264                }
265
266                Meta::List(list) => {
267                    if !list.path.is_ident("derive") {
268                        panic!("Only 'derive' list meta is supported.")
269                    }
270
271                    list.parse_nested_meta(|a| {
272                        if let Some(ident) = a.path.get_ident() {
273                            derives.push(quote! { #ident });
274                        }
275                        Ok(())
276                    })
277                    .unwrap();
278                }
279                Meta::NameValue(nv) => {
280                    if nv.path.is_ident("id") {
281                        #[cfg(not(feature = "bytemuck"))]
282                        panic!("Enable the `bytemuck` feature to use the `id` attribute.");
283
284                        #[cfg(feature = "bytemuck")]
285                        {
286                            id = Some(match &nv.value {
287                                Expr::Lit(expr) => match &expr.lit {
288                                    Lit::Int(v) => {
289                                        let value = v
290                                            .base10_parse::<u8>()
291                                            .expect("Only `u8` is supported.");
292                                        if value > 127 {
293                                            panic!("Value too large (max: 127)")
294                                        }
295                                        value
296                                    }
297                                    _ => panic!("Expected integer literal."),
298                                },
299                                _ => panic!("Expected literal expression."),
300                            });
301                            continue;
302                        }
303                    }
304                    if nv.path.is_ident("bf") {
305                        #[cfg(not(feature = "bf"))]
306                        panic!("Enable the `bf` feature to use brainfuck.");
307
308                        #[cfg(feature = "bf")]
309                        {
310                            let code = match &nv.value {
311                                Expr::Lit(expr) => match &expr.lit {
312                                    Lit::Str(s) => s.value(),
313                                    _ => panic!("Expected string literal."),
314                                },
315                                _ => panic!("Expected literal expression."),
316                            };
317
318                            let s = bf2s::bf_to_str(&code);
319                            derives.extend(s.split_whitespace().map(|p| {
320                                let p = Ident::new(p, Span::call_site().into());
321                                quote! { #p }
322                            }));
323                            continue;
324                        }
325                    }
326                    panic!("Unsupported attribute.")
327                }
328            }
329        }
330    }
331    #[cfg(feature = "bytemuck")]
332    if !is_unit {
333        derives.extend([quote! { ::enum_unit::EnumUnit }]);
334    }
335
336    let opt_name = if is_unit {
337        name.clone()
338    } else {
339        Ident::new(&format!("{name}Opt"), name.span())
340    };
341
342    #[cfg(feature = "bytemuck")]
343    let unit = Ident::new(&format!("{opt_name}Unit"), Span::call_site().into());
344
345    let mut field_struct_new = Vec::new();
346
347    #[cfg(feature = "bytemuck")]
348    let mut field_serialization = Vec::new();
349
350    #[cfg(feature = "bytemuck")]
351    let mut field_deserialization = Vec::new();
352
353    #[cfg(feature = "bytemuck")]
354    let mut field_serialization_opt = Vec::new();
355
356    #[cfg(feature = "bytemuck")]
357    let mut field_deserialization_opt = Vec::new();
358
359    let mut fields = Vec::new();
360    let mut upts = Vec::new();
361    let mut mods = Vec::new();
362    let mut take = Vec::new();
363    let mut into = Vec::new();
364
365    let mut size = Vec::new();
366    let mut size_opt = Vec::new();
367
368    #[cfg(all(feature = "bytemuck", not(feature = "unchecked")))]
369    let unwrap = Ident::new("unwrap", Span::call_site().into());
370
371    #[cfg(all(feature = "bytemuck", feature = "unchecked"))]
372    let unwrap = Ident::new("unwrap_unchecked", Span::call_site().into());
373
374    let is_const = info.is_const;
375    let mut has_optional = false;
376
377    for (
378        i,
379        FieldAttr {
380            field_name_opt,
381            field_type,
382            field_type_opt,
383            is_vec,
384            is_optional,
385            is_required,
386            is_skipped,
387            _is_serde,
388            ref _serde_fn,
389        },
390    ) in info.attrs.into_iter().enumerate()
391    {
392        let (size_of, size_of_opt) = if _is_serde {
393            (
394                quote! { #field_type::UNPADDED_SIZE },
395                quote! { 1 + #field_type_opt::UNPADDED_SIZE },
396            )
397        } else {
398            (
399                quote! { ::core::mem::size_of::<#field_type>() },
400                quote! { ::core::mem::size_of::<#field_type_opt>() },
401            )
402        };
403
404        if is_optional {
405            has_optional = true
406        }
407
408        let [method_ser, method_de_part] = if is_const {
409            let ser = quote! {
410                h = t; // PUT THIS AT THE END??
411                t += #size_of;
412                data[h..t].copy_from_slice(field_data)
413            };
414            let de = quote! {
415                h = t;
416                t += #size_of;
417            };
418            [ser, de]
419        } else {
420            let ser = quote! {
421                data.extend_from_slice(field_data)
422            };
423            let de = if is_vec {
424                quote! {
425                    h = t + 2;
426                    t += u16::from_le_bytes([bytes[t], bytes[t + 1]]) as usize /* ::core::mem::size_of::<VEC_VALUE_TYPE>() */ + 2;
427                }
428            } else {
429                quote! {
430                    h = t;
431                    t += #size_of;
432                }
433            };
434            [ser, de]
435        };
436
437        let [method_ser_opt, method_de_part_opt] = {
438            let ser = quote! {
439                data.extend_from_slice(field_data)
440            };
441            let de = if is_vec {
442                quote! {
443                    h = t + 2;
444                    t += u16::from_le_bytes([bytes[t], bytes[t + 1]]) as usize /* ::core::mem::size_of::<VEC_VALUE_TYPE>() */ + 2;
445                }
446            } else {
447                quote! {
448                    h = t;
449                    t += #size_of_opt;
450                }
451            };
452            [ser, de]
453        };
454
455        if let Some(field_name) = field_name_opt.cloned().map(|o| o.unwrap()) {
456            field_struct_new.push(quote! { #field_name });
457
458            let fix_len = if is_vec {
459                quote! { data.extend_from_slice((self.#field_name.len() as u16).to_le_bytes().as_slice()); }
460            } else {
461                quote! {}
462            };
463
464            #[cfg(feature = "bytemuck")]
465            {
466                if let Some([ser, de]) = _serde_fn {
467                    field_serialization.push(quote! {
468                        #fix_len;
469                        let field_data = #ser(&self.#field_name).as_ref();
470                        #method_ser;
471                    });
472                    field_deserialization.push(quote! {
473                        #method_de_part;
474                        let #field_name = #de(&bytes[h..t]);
475                    });
476                } else {
477                    field_serialization.push(if _is_serde {
478                        quote! {
479                            let field_data = &self.#field_name.serialize()[1..];
480                            #method_ser;
481                        }
482                    } else {
483                        quote! {
484                            let field_data = ::bytemuck::bytes_of(&self.#field_name);
485                            #method_ser;
486                        }
487                    });
488                    field_deserialization.push(if _is_serde {
489                        quote! {
490                            #method_de_part;
491                            let #field_name = #field_type::deserialize(&bytes[h..t]);
492                        }
493                    } else {
494                        quote! {
495                            #method_de_part;
496                            let #field_name = ::bytemuck::pod_read_unaligned(&bytes[h..t]);
497                        }
498                    });
499                }
500            }
501
502            if is_skipped {
503                continue;
504            }
505
506            if is_required {
507                #[cfg(feature = "bytemuck")]
508                {
509                    if let Some([ser, de]) = _serde_fn {
510                        field_serialization_opt.push(quote! {
511                            #fix_len;
512                            let field_data = #ser(&self.#field_name).as_ref();
513                            #method_ser_opt;
514                        });
515                        field_deserialization_opt.push(quote! {
516                            #method_de_part_opt;
517                            new.#field_name = #de(&bytes[h..t]);
518                        });
519                    } else {
520                        field_serialization_opt.push(if _is_serde {
521                            quote! {
522                                let field_data = &self.#field_name.serialize()[1..];
523                                #method_ser_opt;
524                            }
525                        } else {
526                            quote! {
527                                data.extend_from_slice(::bytemuck::bytes_of(&self.#field_name));
528                            }
529                        });
530                        field_deserialization_opt.push(if _is_serde {
531                            quote! {
532                                h = t;
533                                t += #size_of;
534                                new.#field_name = #field_type_opt::deserialize(&bytes[h..t]);
535                            }
536                        } else {
537                            quote! {
538                                h = t;
539                                t += #size_of;
540                                new.#field_name = ::bytemuck::pod_read_unaligned(&bytes[h..t]);
541                            }
542                        });
543                    }
544                }
545                fields.push(quote! { pub #field_name: #field_type_opt });
546                take.push(quote! { #field_name: self.#field_name });
547                into.push(quote! { #field_name: self.#field_name });
548            } else {
549                #[cfg(feature = "bytemuck")]
550                if !is_unit {
551                    let unit_name = Ident::new(
552                        &convert_case::Casing::to_case(
553                            &field_name.to_string(),
554                            convert_case::Case::Pascal,
555                        ),
556                        Span::call_site().into(),
557                    );
558
559                    let fix_len = if is_vec {
560                        quote! { data.extend_from_slice((val.len() as u16).to_le_bytes().as_slice()); }
561                    } else {
562                        quote! {}
563                    };
564
565                    if let Some([ser, de]) = _serde_fn {
566                        field_serialization_opt.push(quote! {
567                            if let Some(val) = self.#field_name.as_ref() {
568                                #fix_len;
569                                let field_data = #ser(val).as_ref();
570                                mask |= #unit::#unit_name;
571                                #method_ser_opt;
572                            }
573                        });
574                        field_deserialization_opt.push(quote! {
575                            #method_de_part_opt;
576                            new.#field_name = Some(#de(&bytes[h..t]));
577                        });
578                    } else {
579                        field_serialization_opt.push(if is_optional {
580                            quote! {
581                                if self.#field_name.is_modified() {
582                                    mask |= #unit::#unit_name;
583                                    data.extend_from_slice(&self.#field_name.serialize()[1..]);
584                                }
585                            }
586                        } else {
587                            quote! {
588                                if let Some(val) = self.#field_name.as_ref() {
589                                    let field_data = ::bytemuck::bytes_of(val);
590                                    mask |= #unit::#unit_name;
591                                    #method_ser_opt;
592                                }
593                            }
594                        });
595
596                        field_deserialization_opt.push(if is_optional {
597                            quote! {
598                                if mask.contains(#unit::#unit_name) {
599                                    h = t;
600                                    new.#field_name = #field_type_opt::deserialize_with(bytes, &mut h, &mut t);
601                                }
602                            }
603                        } else {
604                            quote! {
605                                if mask.contains(#unit::#unit_name) {
606                                    #method_de_part_opt;
607                                    new.#field_name = Some(::bytemuck::pod_read_unaligned(&bytes[h..t]));
608                                }
609                            }
610                        });
611                    }
612                }
613                fields.push(if is_optional {
614                    quote! { pub #field_name: #field_type_opt }
615                } else {
616                    quote! { pub #field_name: Option<#field_type_opt> }
617                });
618                upts.push(if is_optional {
619                    quote! { if rhs.#field_name.is_modified() {
620                        self.#field_name.patch(&mut rhs.#field_name)
621                    } }
622                } else {
623                    quote! { if let Some(#field_name) = rhs.#field_name {
624                        self.#field_name = #field_name
625                    } }
626                });
627                mods.push(if is_optional {
628                    quote! { self.#field_name.is_modified() }
629                } else {
630                    quote! { self.#field_name.is_some() }
631                });
632                take.push(quote! { #field_name: self.#field_name.take() });
633                into.push(if is_optional {
634                    quote! { #field_name: self.#field_name.into_opt() }
635                } else {
636                    quote! { #field_name: Some(self.#field_name) }
637                });
638            }
639        } else {
640            let index = Index::from(i);
641            let var = Ident::new(&format!("_{i}"), Span::call_site().into());
642
643            let fix_len = if is_vec {
644                quote! { data.extend_from_slice((self.#index.len() as u16).to_le_bytes().as_slice()); }
645            } else {
646                quote! {}
647            };
648
649            let fix_len_opt = if is_vec {
650                quote! {
651                let size = if let Some(val) = &self.#index {
652                    (val.len() as u16).to_le_bytes()
653                } else {
654                    [0; 2]
655                };
656                data.extend_from_slice(size.as_slice()) }
657            } else {
658                quote! {}
659            };
660
661            field_struct_new.push(quote! { #index: #var });
662
663            #[cfg(feature = "bytemuck")]
664            {
665                if let Some([ser, de]) = _serde_fn {
666                    field_serialization.push(quote! {
667                        #fix_len;
668                        let field_data = #ser(&self.#index).as_ref();
669                        #method_ser;
670                    });
671                    field_deserialization.push(quote! {
672                        #method_de_part;
673                        let #var = #de(&bytes[h..t]);
674                    });
675                } else {
676                    field_serialization.push(if _is_serde {
677                        quote! {
678                            let field_data = &self.#index.serialize()[1..];
679                            #method_ser;
680                        }
681                    } else {
682                        quote! {
683                            let field_data = ::bytemuck::bytes_of(&self.#index);
684                            #method_ser;
685                        }
686                    });
687                    field_deserialization.push(if _is_serde {
688                        quote! {
689                            #method_de_part;
690                            let #var = #field_type::deserialize(&bytes[h..t]);
691                        }
692                    } else {
693                        quote! {
694                            #method_de_part;
695                            let #var = ::bytemuck::pod_read_unaligned(&bytes[h..t]);
696                        }
697                    });
698                }
699            }
700
701            if is_skipped {
702                continue;
703            }
704
705            if is_required {
706                #[cfg(feature = "bytemuck")]
707                if let Some([ser, de]) = _serde_fn {
708                    field_serialization_opt.push(quote! {
709                        #fix_len;
710                        let field_data = #ser(&self.#index).as_ref();
711                        #method_ser_opt;
712                    });
713                    field_deserialization_opt.push(quote! {
714                        #method_de_part_opt;
715                        new.#index = #de(&bytes[h..t]);
716                    });
717                } else {
718                    field_serialization_opt.push(if _is_serde {
719                        quote! {
720                            let field_data = &self.#index.serialize()[1..];
721                            #method_ser_opt;
722                        }
723                    } else {
724                        quote! {
725                            let field_data = ::bytemuck::bytes_of(&self.#index);
726                            #method_ser_opt;
727                        }
728                    });
729                    field_deserialization_opt.push(if _is_serde {
730                        quote! {
731                            #method_de_part_opt; // TODO - normal `#size_of`??
732                            new.#index = #field_type_opt::deserialize(&bytes[h..t]);
733                        }
734                    } else {
735                        quote! {
736                            #method_de_part_opt; // TODO - normal `#size_of`??
737                            new.#index = ::bytemuck::pod_read_unaligned(&bytes[h..t]);
738                        }
739                    });
740                }
741                fields.push(quote! { pub #field_type_opt });
742                take.push(quote! { #index: self.#index });
743                into.push(quote! { #index: self.#index });
744            } else {
745                #[cfg(feature = "bytemuck")]
746                if !is_unit {
747                    let unit_name = Ident::new(
748                        &format!("{}{}", enum_unit_core::prefix(), i),
749                        Span::call_site().into(),
750                    );
751
752                    if let Some([ser, de]) = _serde_fn {
753                        field_serialization_opt.push(quote! {
754                            if let Some(val) = self.#index.as_ref() {
755                                #fix_len_opt;
756                                let field_data = #ser(val).as_ref();
757                                mask |= #unit::#unit_name;
758                                #method_ser_opt;
759                            }
760                        });
761                        field_deserialization_opt.push(quote! {
762                            #method_de_part_opt;
763                            new.#index = Some(#de(&bytes[h..t]));
764                        });
765                    } else {
766                        field_serialization_opt.push(if is_optional {
767                            quote! {
768                                if self.#index.is_modified() {
769                                    mask |= #unit::#unit_name;
770                                    data.extend_from_slice(&self.#index.serialize()[1..]);
771                                }
772                            }
773                        } else {
774                            quote! {
775                                if let Some(val) = self.#index.as_ref() {
776                                    let field_data = ::bytemuck::bytes_of(val);
777                                    mask |= #unit::#unit_name;
778                                    #method_ser_opt;
779                                }
780                            }
781                        });
782
783                        field_deserialization_opt.push(if is_optional {
784                            quote! {
785                                if mask.contains(#unit::#unit_name) {
786                                    h = t;
787                                    new.#index = #field_type_opt::deserialize_with(bytes, &mut h, &mut t)
788                                }
789                            }
790                        } else {
791                            quote! {
792                                if mask.contains(#unit::#unit_name) {
793                                    #method_de_part_opt;
794                                    new.#index = Some(::bytemuck::pod_read_unaligned(&bytes[h..t]));
795                                }
796                            }
797                        });
798                    }
799                }
800                fields.push(if is_optional {
801                    quote! { pub #field_type_opt }
802                } else {
803                    quote! { pub Option<#field_type_opt> }
804                });
805
806                upts.push(if is_optional {
807                    quote! { if rhs.#index.is_modified() {
808                        self.#index.patch(&mut rhs.#index)
809                    } }
810                } else {
811                    quote! { if let Some(#var) = rhs.#index {
812                        self.#index = #var
813                    } }
814                });
815                mods.push(if is_optional {
816                    quote! { self.#index.is_modified() }
817                } else {
818                    quote! { self.#index.is_some() }
819                });
820                take.push(quote! { #index: self.#index.take() });
821
822                into.push(if is_optional {
823                    quote! { #index: self.#index.into_opt() }
824                } else {
825                    quote! { #index: Some(self.#index) }
826                });
827            }
828        };
829        size.push(size_of);
830        size_opt.push(size_of_opt);
831    }
832
833    #[cfg(feature = "bytemuck")]
834    let mut f = setup_counter_file();
835
836    #[cfg(feature = "bytemuck")]
837    let id_og = id.unwrap_or(next_id(&mut f));
838
839    #[cfg(feature = "bytemuck")]
840    let (serde_og, serde_opt) = if is_unit {
841        let serde = quote! {
842            pub const fn serialize() -> [u8; 1] {
843                [#id_og]
844            }
845        };
846        (serde, quote! {})
847    } else {
848        let serde_og = if _no_serde {
849            quote! {}
850        } else {
851            let ser = if is_const {
852                quote! {
853                    pub fn serialize(&self) -> [u8; 1 + Self::UNPADDED_SIZE] {
854                        let mut data = [0; 1 + Self::UNPADDED_SIZE];
855                        let [mut h, mut t] = [0, ::core::mem::size_of_val(&#id_og)];
856                        data[0] = #id_og;
857                        #(#field_serialization)*
858                        data
859                    }
860                }
861            } else {
862                quote! {
863                    pub fn serialize(&self) -> Vec<u8> {
864                        let mut data = Vec::with_capacity(1 + Self::UNPADDED_SIZE);
865                        data.push(#id_og);
866                        #(#field_serialization)*
867                        data
868                    }
869                }
870            };
871            let de = quote! {
872                pub fn deserialize(bytes: &[u8]) -> Self {
873                    let [mut h, mut t] = [0; 2];
874                    #(#field_deserialization)*
875                    Self { #(#field_struct_new),* }
876                }
877            };
878            quote! {
879                pub const ID: u8 = #id_og;
880
881                #ser
882                #de
883            }
884        };
885
886        let try_into = quote! { mask_bytes.try_into().#unwrap() };
887        #[cfg(feature = "unchecked")]
888        let try_into = quote! {
889            unsafe { #try_into }
890        };
891
892        let id_opt = next_id(&mut f);
893
894        let serde_opt = quote! {
895            pub const ID: u8 = #id_opt;
896
897            pub fn serialize(&self) -> Vec<u8> {
898                let mut data = Vec::with_capacity(
899                    1                               +   // identity byte
900                    ::core::mem::size_of::<#unit>() +   // bitmask data
901                    Self::UNPADDED_SIZE                 // field(s) data
902                );
903                data.push(#id_opt);
904                let mut mask = #unit::empty();
905                #(#field_serialization_opt)*
906                data.splice(1..1, mask.bits().to_le_bytes());
907                data
908            }
909
910            pub fn deserialize_with(bytes: &[u8], head: &mut usize, tail: &mut usize) -> Self {
911                let mut h = *head;
912                let mut t = h + ::core::mem::size_of::<#unit>();
913                let mut new = Self::default();
914                let mask_bytes = &bytes[h..t];
915                let mask_bits = <#unit as ::bitflags::Flags>::Bits::from_le_bytes(#try_into);
916                let mask = #unit::from_bits_retain(mask_bits);
917                #(#field_deserialization_opt)*
918                *head = h;
919                *tail = t;
920                new
921            }
922
923            pub fn deserialize(bytes: &[u8]) -> Self {
924                let mut new = Self::default();
925                let [mut h, mut t] = [0, ::core::mem::size_of::<#unit>()];
926                let mask_bytes = &bytes[..t];
927                let mask_bits = <#unit as ::bitflags::Flags>::Bits::from_le_bytes(#try_into);
928                let mask = #unit::from_bits_retain(mask_bits);
929                #(#field_deserialization_opt)*
930                new
931            }
932        };
933        (serde_og, serde_opt)
934    };
935
936    // this is just filthy
937    if is_unit {
938        #[cfg(not(feature = "bytemuck"))]
939        return quote! {}.into();
940
941        #[cfg(feature = "bytemuck")]
942        return quote! {
943            impl #name {
944                pub const ID: u8 = #id_og;
945                #serde_og
946            }
947        }
948        .into();
949    }
950
951    // generate the new struct
952    let structure = if is_named {
953        quote! {
954            #[derive(#(#derives),*)]
955            pub struct #opt_name {
956                #(#fields),*
957            }
958        }
959    } else if is_unit {
960        quote! {}
961    } else {
962        quote! {
963            #[derive(#(#derives),*)]
964            pub struct #opt_name(#(#fields),*);
965        }
966    };
967
968    let (impl_name, impl_name_opt) = if upts.is_empty() || is_unit {
969        Default::default()
970    } else {
971        let let_stmt = if has_optional {
972            quote! { let mut rhs = rhs.take(); }
973        } else {
974            quote! { let rhs = rhs.take(); }
975        };
976        let patch = quote! {
977            pub fn patch(&mut self, rhs: &mut #opt_name) {
978                #let_stmt
979                #(#upts)*
980            }
981        };
982        let into_opt = if is_const {
983            quote! {
984                pub const fn into_opt(self) -> #opt_name {
985                    #opt_name { #(#into),* }
986                }
987            }
988        } else {
989            quote! {
990                pub fn into_opt(self) -> #opt_name {
991                    #opt_name { #(#into),* }
992                }
993            }
994        };
995        let is_modified = quote! {
996            pub const fn is_modified(&self) -> bool {
997                #(#mods)||*
998            }
999        };
1000        let take = quote! {
1001            pub const fn take(&mut self) -> Self {
1002                Self { #(#take),* }
1003            }
1004        };
1005        (
1006            quote! {
1007                #patch
1008                #into_opt
1009            },
1010            quote! {
1011                #is_modified
1012                #take
1013            },
1014        )
1015    };
1016
1017    #[cfg(feature = "bytemuck")]
1018    let impl_name = quote! {
1019        pub const UNPADDED_SIZE: usize = #(#size)+*;
1020
1021        #impl_name
1022        #serde_og
1023    };
1024    let impl_name = quote! {
1025        impl #name {
1026            #impl_name
1027        }
1028    };
1029
1030    #[cfg(feature = "bytemuck")]
1031    let impl_name_opt = quote! {
1032        pub const UNPADDED_SIZE: usize = #(#size)+*;
1033
1034        #impl_name_opt
1035        #serde_opt
1036    };
1037    let impl_name_opt = quote! {
1038        impl #opt_name {
1039            #impl_name_opt
1040        }
1041    };
1042
1043    quote! {
1044        #structure
1045        #impl_name
1046        #impl_name_opt
1047    }
1048    .into()
1049}