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 std::fs::create_dir_all(unsafe {
31 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 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 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 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 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 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 let input = parse_macro_input!(input as DeriveInput);
206
207 let name = &input.ident;
209
210 #[cfg(feature = "bytemuck")]
212 let mut id = None;
213
214 #[allow(unused_mut)]
215 let mut is_unit = false;
216
217 let mut is_named = false;
219
220 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 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; 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 + 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 + 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; new.#index = #field_type_opt::deserialize(&bytes[h..t]);
733 }
734 } else {
735 quote! {
736 #method_de_part_opt; 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 + ::core::mem::size_of::<#unit>() + Self::UNPADDED_SIZE );
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 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 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}