1extern crate proc_macro;
200mod macro_args;
201
202use macro_args::MacroArgs;
203use proc_macro2::{Span, TokenStream};
204use quote::{ToTokens, format_ident, quote, quote_spanned};
205use syn::token::{Comma, Where};
206use syn::{
207 Field, Fields, Generics, Ident, Index, ItemStruct, Path, Type, TypePath, parse::Result as SynResult, punctuated::Punctuated,
208 spanned::Spanned,
209};
210
211enum TailKind {
212 Slice(Box<Type>),
213 Str,
214 TraitObject(Path),
215}
216
217enum FieldIdent {
218 Named(Ident),
219 Unnamed(Index),
220}
221
222impl ToTokens for FieldIdent {
223 fn to_tokens(&self, tokens: &mut TokenStream) {
224 match self {
225 Self::Named(ident) => ident.to_tokens(tokens),
226 Self::Unnamed(index) => index.to_tokens(tokens),
227 }
228 }
229}
230
231struct StructInfo<'a> {
232 struct_name: &'a Ident,
233 struct_generics: &'a Generics,
234 header_fields: Box<[&'a Field]>,
235 header_field_idents: Box<[FieldIdent]>,
236 header_param_idents: Box<[Ident]>,
237 header_types: Vec<Type>,
238 tail_field: &'a Field,
239 tail_field_ident: FieldIdent,
240 tail_param_ident: Ident,
241 tail_kind: TailKind,
242}
243
244impl<'a> StructInfo<'a> {
245 fn new(input_struct: &'a ItemStruct) -> SynResult<Self> {
246 match &input_struct.fields {
247 Fields::Named(named_fields) => Self::process_fields(input_struct, &named_fields.named),
248 Fields::Unnamed(unnamed_fields) => Self::process_fields(input_struct, &unnamed_fields.unnamed),
249 Fields::Unit => Err(syn::Error::new_spanned(input_struct, "Unit structs are not supported")),
250 }
251 }
252
253 fn process_fields(input_struct: &'a ItemStruct, fields: &'a Punctuated<Field, Comma>) -> SynResult<Self> {
254 if fields.is_empty() {
255 return Err(syn::Error::new_spanned(input_struct, "Struct must have at least one field"));
256 }
257
258 let header_fields: Vec<_> = fields.iter().take(fields.len() - 1).collect();
259 let tail_field = fields.last().unwrap();
260
261 let tail_kind = match &tail_field.ty {
262 Type::Path(type_path) if type_path.path.is_ident("str") => TailKind::Str,
263 Type::Path(TypePath { path, .. }) if path.segments.last().is_some_and(|s| s.ident == "str") => TailKind::Str,
264 Type::Slice(type_slice) => TailKind::Slice(type_slice.elem.clone()),
265
266 Type::TraitObject(type_trait_object) => {
267 for bound in &type_trait_object.bounds {
269 if let syn::TypeParamBound::Trait(trait_bound) = bound {
270 if trait_bound.lifetimes.is_some() {
271 return Err(syn::Error::new_spanned(
272 trait_bound,
273 "Higher-rank trait bounds (e.g., `for<'a> dyn Trait<'a>`) are not supported for the tail field.",
274 ));
275 }
276 }
277 }
278
279 let trait_path = type_trait_object
281 .bounds
282 .iter()
283 .find_map(|bound| {
284 if let syn::TypeParamBound::Trait(trait_bound) = bound {
285 Some(&trait_bound.path)
286 } else {
287 None }
289 })
290 .unwrap();
291
292 TailKind::TraitObject(trait_path.clone())
293 }
294
295 _ => {
296 return Err(syn::Error::new_spanned(
297 &tail_field.ty,
298 "Last field must be a dynamically sized type like [T], str, or dyn Trait",
299 ));
300 }
301 };
302
303 let header_param_idents: Vec<_> = header_fields
304 .iter()
305 .enumerate()
306 .map(|(i, field)| field.ident.as_ref().map_or_else(|| format_ident!("f{i}"), Clone::clone))
307 .collect();
308
309 let header_field_idents: Vec<_> = header_fields
310 .iter()
311 .enumerate()
312 .map(|(i, field)| {
313 field
314 .ident
315 .as_ref()
316 .map_or_else(|| FieldIdent::Unnamed(Index::from(i)), |ident| FieldIdent::Named(ident.clone()))
317 })
318 .collect();
319
320 let header_types: Vec<_> = header_fields.iter().map(|field| field.ty.clone()).collect();
321
322 let tail_param_ident = tail_field
323 .ident
324 .as_ref()
325 .map_or_else(|| format_ident!("f{}", header_fields.len()), Clone::clone);
326
327 let tail_field_ident = tail_field.ident.as_ref().map_or_else(
328 || FieldIdent::Unnamed(Index::from(header_fields.len())),
329 |ident| FieldIdent::Named(ident.clone()),
330 );
331
332 Ok(Self {
333 struct_name: &input_struct.ident,
334 struct_generics: &input_struct.generics,
335 header_fields: header_fields.into_boxed_slice(),
336 header_field_idents: header_field_idents.into_boxed_slice(),
337 header_param_idents: header_param_idents.into_boxed_slice(),
338 header_types,
339 tail_field,
340 tail_field_ident,
341 tail_param_ident,
342 tail_kind,
343 })
344 }
345}
346
347fn header_layout(macro_args: &MacroArgs, struct_info: &StructInfo, for_trait: bool) -> TokenStream {
348 let tail_field_ident = &struct_info.tail_field_ident;
349
350 let header_field_types: Vec<_> = struct_info.header_fields.iter().map(|field| &field.ty).collect();
351
352 if header_field_types.is_empty() {
353 return quote! {
354 let layout = ::core::alloc::Layout::from_size_align(0, 1).unwrap();
355 };
356 }
357
358 let fat_payload = if for_trait {
359 quote! { vtable }
360 } else {
361 quote! { 0_usize }
362 };
363
364 let tail_type = match &struct_info.tail_kind {
365 TailKind::Slice(elem_type) => quote! { #elem_type },
366 TailKind::Str => quote! { u8 },
367 TailKind::TraitObject(_) => {
368 let generic_name = ¯o_args.generic_name;
369 quote! { #generic_name }
370 }
371 };
372
373 quote! {
374 let buffer = ::core::mem::MaybeUninit::<(#( #header_field_types, )* #tail_type)>::uninit();
375 let (offset, align) = unsafe {
376 let head_ptr: *const Self = ::core::mem::transmute((&raw const buffer, #fat_payload));
377 let tail_ptr = &raw const (*head_ptr).#tail_field_ident;
378 (
379 (tail_ptr as *const u8).offset_from_unsigned(head_ptr as *const u8),
380 ::core::mem::align_of_val::<Self>(&*head_ptr)
381 )
382 };
383
384 let layout = ::core::alloc::Layout::from_size_align(offset, align).unwrap();
385 }
386}
387
388fn tail_layout<T: quote::ToTokens>(tail_type: &T, span: Span) -> TokenStream {
389 quote_spanned! { span => ::core::alloc::Layout::array::<#tail_type>(len).expect("Array exceeds maximum size allowed of isize::MAX") }
390}
391
392fn dealloc_path(no_std: bool) -> TokenStream {
393 if no_std {
394 quote! { ::alloc::alloc::dealloc }
395 } else {
396 quote! { ::std::alloc::dealloc }
397 }
398}
399
400fn box_path(no_std: bool) -> TokenStream {
401 if no_std {
402 quote! { ::alloc::boxed::Box }
403 } else {
404 quote! { ::std::boxed::Box }
405 }
406}
407
408fn guard_type(macro_args: &MacroArgs) -> TokenStream {
409 let dealloc_path = dealloc_path(macro_args.no_std);
410
411 quote! {
412 struct Guard<T> {
413 mem_ptr: *mut u8,
414 tail_ptr: *mut T,
415 initialized: usize,
416 layout: ::core::alloc::Layout,
417 }
418
419 impl<T> Drop for Guard<T> {
420 fn drop(&mut self) {
421 unsafe {
422 let slice_ptr = ::core::ptr::slice_from_raw_parts_mut(self.tail_ptr, self.initialized);
423 ::core::ptr::drop_in_place(slice_ptr);
424 #dealloc_path(self.mem_ptr, self.layout);
425 }
426 }
427 }
428 }
429}
430
431fn header_params(struct_info: &StructInfo) -> Vec<TokenStream> {
432 struct_info
433 .header_fields
434 .iter()
435 .enumerate()
436 .map(|(i, field)| {
437 let param_ident = &struct_info.header_param_idents[i];
438 let field_ty = &field.ty;
439 quote! { #param_ident: #field_ty }
440 })
441 .collect()
442}
443
444fn header_field_writes(struct_info: &StructInfo) -> Vec<TokenStream> {
445 struct_info
446 .header_field_idents
447 .iter()
448 .enumerate()
449 .map(|(i, field_ident)| {
450 let tuple_idx = syn::Index::from(i);
451 quote! { ::core::ptr::write_unaligned(&raw mut ((*fat_ptr).#field_ident), args.#tuple_idx);}
452 })
453 .collect()
454}
455
456fn args_tuple_assignment(struct_info: &StructInfo) -> TokenStream {
457 let header_param_idents = &struct_info.header_param_idents;
458 let tail_param_ident = &struct_info.tail_param_ident;
459
460 quote! {
461 let args = ( #( #header_param_idents, )* #tail_param_ident, );
462 }
463}
464
465fn alloc(no_std: bool) -> TokenStream {
466 let (alloc_path, handle_alloc_error) = if no_std {
467 (quote! { ::alloc::alloc::alloc }, quote! { panic!("out of memory") })
468 } else {
469 (quote! { ::std::alloc::alloc }, quote! { ::std::alloc::handle_alloc_error(layout) })
470 };
471
472 quote! {
473 let mem_ptr = #alloc_path(layout);
474 if mem_ptr.is_null() {
475 #handle_alloc_error
476 }
477 }
478}
479
480fn alloc_zst(box_path: &TokenStream, for_trait: bool) -> TokenStream {
481 let mem_ptr = quote! { let mem_ptr = ::core::ptr::NonNull::<()>::dangling().as_ptr(); };
482
483 let fat_ptr = if for_trait {
484 quote! { let fat_ptr = ::core::mem::transmute::<(*mut (), *const ()), *mut Self>((mem_ptr, vtable)); }
485 } else {
486 quote! { let fat_ptr = ::core::mem::transmute::<(*mut (), usize), *mut Self>((mem_ptr, 0_usize)); }
487 };
488
489 let box_from_raw = quote! { #box_path::from_raw(fat_ptr) };
490
491 quote! {
492 #mem_ptr
493 #fat_ptr
494 #box_from_raw
495 }
496}
497
498fn factory_for_slice_arg(macro_args: &MacroArgs, struct_info: &StructInfo, tail_elem_type: &Type) -> TokenStream {
499 let copy_bound_tokens: syn::WherePredicate = syn::parse_quote_spanned! {tail_elem_type.span()=>
500 #tail_elem_type: ::core::marker::Copy
501 };
502
503 let mut factory_where_clause = struct_info.struct_generics.where_clause.as_ref().map_or_else(
504 || syn::WhereClause {
505 where_token: Where::default(),
506 predicates: Punctuated::new(),
507 },
508 Clone::clone,
509 );
510
511 factory_where_clause.predicates.push(copy_bound_tokens);
512
513 let alloc = alloc(macro_args.no_std);
514 let box_path = box_path(macro_args.no_std);
515 let make_zst = alloc_zst(&box_path, false);
516
517 let tail_layout = tail_layout(tail_elem_type, struct_info.tail_field.ty.span());
518 let header_layout = header_layout(macro_args, struct_info, false);
519 let tuple_assignment = args_tuple_assignment(struct_info);
520 let header_field_writes = header_field_writes(struct_info);
521 let header_params = header_params(struct_info);
522
523 let factory_name = format_ident!("{}_from_slice", ¯o_args.base_factory_name);
524 let visibility = ¯o_args.visibility;
525
526 let tail_param = &struct_info.tail_param_ident;
527 let tail_field = &struct_info.tail_field_ident;
528 let struct_name = &struct_info.struct_name;
529 let tail_args_tuple_idx = Index::from(struct_info.header_fields.len());
530
531 let factory_doc = format!("Creates an instance of `Box<{struct_name}>`.");
532
533 quote! {
534 #[doc = #factory_doc]
535 #[allow(clippy::let_unit_value)]
536 #[allow(clippy::zst_offset)]
537 #[allow(clippy::transmute_undefined_repr)]
538 #visibility fn #factory_name (
539 #( #header_params, )*
540 #tail_param: &[#tail_elem_type]
541 ) -> #box_path<Self> #factory_where_clause {
542 #tuple_assignment
543
544 let s = args.#tail_args_tuple_idx.as_ref();
545 let len = s.len();
546
547 #header_layout
548 let layout = layout.extend(#tail_layout).expect("Struct exceeds maximum size allowed of isize::MAX").0;
549 let layout = layout.pad_to_align();
550
551 unsafe {
552 if layout.size() == 0 {
553 #make_zst
554 } else {
555 #alloc
556
557 let fat_ptr = ::core::mem::transmute::<(*mut u8, usize), *mut Self>((mem_ptr, len));
558 ::core::debug_assert_eq!(::core::alloc::Layout::for_value(&*fat_ptr), layout);
559
560 #( #header_field_writes )*
561
562 let tail_ptr = (&raw mut (*fat_ptr).#tail_field).cast::<#tail_elem_type>();
564 ::core::ptr::copy_nonoverlapping(s.as_ptr(), tail_ptr, len);
565
566 #box_path::from_raw(fat_ptr)
567 }
568 }
569 }
570 }
571}
572
573fn factory_for_iter_arg(macro_args: &MacroArgs, struct_info: &StructInfo, tail_type: &Type) -> TokenStream {
574 let guard_type_tokens = guard_type(macro_args);
575 let alloc = alloc(macro_args.no_std);
576 let box_path = box_path(macro_args.no_std);
577 let make_zst = alloc_zst(&box_path, false);
578
579 let tail_layout = tail_layout(tail_type, struct_info.tail_field.ty.span());
580 let header_layout = header_layout(macro_args, struct_info, false);
581 let tuple_assignment = args_tuple_assignment(struct_info);
582 let header_field_writes = header_field_writes(struct_info);
583 let header_params = header_params(struct_info);
584
585 let visibility = ¯o_args.visibility;
586 let factory_name = ¯o_args.base_factory_name;
587 let iter_generic_param = ¯o_args.generic_name;
588
589 let tail_param = &struct_info.tail_param_ident;
590 let tail_field = &struct_info.tail_field_ident;
591 let struct_name = &struct_info.struct_name;
592 let tail_args_tuple_idx = Index::from(struct_info.header_fields.len());
593
594 let factory_doc = format!("Creates an instance of `Box<{struct_name}>`.");
595
596 quote! {
597 #[doc = #factory_doc]
598 #[allow(clippy::let_unit_value)]
599 #[allow(clippy::zst_offset)]
600 #[allow(clippy::transmute_undefined_repr)]
601 #visibility fn #factory_name <#iter_generic_param> (
602 #( #header_params, )*
603 #tail_param: #iter_generic_param
604 ) -> #box_path<Self>
605 where
606 #iter_generic_param: ::core::iter::IntoIterator<Item = #tail_type>,
607 <#iter_generic_param as ::core::iter::IntoIterator>::IntoIter: ::core::iter::ExactSizeIterator
608 {
609 #guard_type_tokens
610 #tuple_assignment
611
612 let iter = args.#tail_args_tuple_idx.into_iter();
613 let len = iter.len();
614
615 #header_layout
616 let layout = layout.extend(#tail_layout).expect("Struct exceeds maximum size allowed of isize::MAX").0;
617 let layout = layout.pad_to_align();
618
619 unsafe {
620 if layout.size() == 0 {
621 #make_zst
622 } else {
623 #alloc
624
625 let fat_ptr = ::core::mem::transmute::<(*mut u8, usize), *mut Self>((mem_ptr, len));
626 ::core::debug_assert_eq!(::core::alloc::Layout::for_value(&*fat_ptr), layout);
627
628 #( #header_field_writes )*
629
630 let tail_ptr = ::core::ptr::addr_of_mut!((*fat_ptr).#tail_field).cast::<#tail_type>();
632 let mut guard = Guard { mem_ptr, tail_ptr, layout, initialized: 0 };
633 iter.for_each(|element| {
634 if guard.initialized == len {
635 panic!("Mismatch between iterator-reported length and the number of items produced by the iterator");
636 }
637
638 ::core::ptr::write(tail_ptr.add(guard.initialized), element);
639 guard.initialized += 1;
640 });
641
642 if guard.initialized != len {
643 panic!("Mismatch between iterator-reported length and the number of items produced by the iterator");
644 }
645
646 ::std::mem::forget(guard);
647
648 #box_path::from_raw(fat_ptr)
649 }
650 }
651 }
652 }
653}
654
655fn destructurer_iterator_type(macro_args: &MacroArgs, struct_info: &StructInfo, tail_type: &Type) -> TokenStream {
656 let dealloc_path = dealloc_path(macro_args.no_std);
657
658 let visibility = ¯o_args.visibility;
659 let iterator_name = macro_args
660 .iterator_name
661 .clone()
662 .unwrap_or_else(|| Ident::new(&format!("{}Iter", struct_info.struct_name), proc_macro2::Span::call_site()));
663
664 let struct_name = &struct_info.struct_name;
665 let struct_generics = &struct_info.struct_generics;
666 let (impl_generics, ty_generics, where_clause) = struct_info.struct_generics.split_for_impl();
667
668 let factory_doc = format!("Iterator type for a destructured `Box<{struct_name}>`");
669
670 quote! {
671 #[doc = #factory_doc]
672 #visibility struct #iterator_name #struct_generics #where_clause {
673 ptr: *mut #tail_type,
674 index: usize,
675 len: usize,
676
677 free_ptr: *mut u8,
678 layout: ::core::alloc::Layout,
679 }
680
681 impl #impl_generics ::core::iter::Iterator for #iterator_name #ty_generics #where_clause {
682 type Item = #tail_type;
683
684 fn next(&mut self) -> Option<Self::Item> {
685 if self.index >= self.len {
686 return None;
687 }
688 #[allow(clippy::zst_offset)]
689 let value = unsafe { self.ptr.add(self.index).read() };
690 self.index += 1;
691 Some(value)
692 }
693 }
694
695 impl #impl_generics ::core::ops::Drop for #iterator_name #ty_generics #where_clause {
696 fn drop(&mut self) {
697 unsafe { #dealloc_path(self.free_ptr, self.layout) }
698 }
699 }
700 }
701}
702
703fn destructurer_with_iter(macro_args: &MacroArgs, struct_info: &StructInfo) -> TokenStream {
704 let box_path = box_path(macro_args.no_std);
705
706 let header_fields = &struct_info.header_field_idents;
707 let header_types = &struct_info.header_types;
708
709 let visibility = ¯o_args.visibility;
710 let destructurer_name = ¯o_args.base_destructurer_name;
711
712 let tail_field = &struct_info.tail_field_ident;
713 let struct_name = &struct_info.struct_name;
714 let iterator_name = macro_args
715 .iterator_name
716 .clone()
717 .unwrap_or_else(|| Ident::new(&format!("{}Iter", struct_info.struct_name), proc_macro2::Span::call_site()));
718 let (_impl_generics, ty_generics, _where_clause) = struct_info.struct_generics.split_for_impl();
719
720 let factory_doc = format!("Destructures an instance of `Box<{struct_name}>`, returning the tail slice as an iterator.");
721
722 quote! {
723 #[doc = #factory_doc]
724 #visibility fn #destructurer_name(
725 this: #box_path<Self>,
726 ) -> ( #( #header_types, )* #iterator_name #ty_generics)
727 {
728 let layout = ::core::alloc::Layout::for_value(&*this);
729 let len = this.#tail_field.len();
730 let this = #box_path::into_raw(this);
731 unsafe {
732 (
733 #( (&raw mut (*this).#header_fields).read(), )*
734 #iterator_name {
735 ptr: (*this).#tail_field.as_mut_ptr(),
736 index: 0,
737 len,
738
739 free_ptr: this.cast(),
740 layout,
741 }
742 )
743 }
744 }
745 }
746}
747
748fn factory_for_str_arg(macro_args: &MacroArgs, struct_info: &StructInfo) -> TokenStream {
749 let alloc = alloc(macro_args.no_std);
750 let box_path = box_path(macro_args.no_std);
751 let make_zst = alloc_zst(&box_path, false);
752
753 let tail_layout = tail_layout("e! { u8 }, struct_info.tail_field.ty.span());
754 let header_layout = header_layout(macro_args, struct_info, false);
755 let tuple_assignment = args_tuple_assignment(struct_info);
756 let header_field_writes = header_field_writes(struct_info);
757 let header_params = header_params(struct_info);
758
759 let factory_name = ¯o_args.base_factory_name;
760 let visibility = ¯o_args.visibility;
761
762 let struct_name = &struct_info.struct_name;
763 let tail_param = &struct_info.tail_param_ident;
764 let tail_field = &struct_info.tail_field_ident;
765 let tail_type = &struct_info.tail_field.ty;
766 let tail_args_tuple_idx = Index::from(struct_info.header_fields.len());
767
768 let factory_doc = format!("Creates an instance of `Box<{struct_name}>`.");
769
770 quote! {
771 #[doc = #factory_doc]
772 #[allow(clippy::let_unit_value)]
773 #[allow(clippy::zst_offset)]
774 #[allow(clippy::transmute_undefined_repr)]
775 #visibility fn #factory_name(
776 #( #header_params, )*
777 #tail_param: impl ::core::convert::AsRef<str>
778 ) -> #box_path<Self> {
779 #tuple_assignment
780
781 ::core::assert_eq!(::core::any::TypeId::of::<#tail_type>(), ::core::any::TypeId::of::<str>());
782 let s = args.#tail_args_tuple_idx.as_ref();
783 let len = s.len();
784
785 #header_layout
786 let layout = layout.extend(#tail_layout).expect("Struct exceeds maximum size allowed of isize::MAX").0;
787 let layout = layout.pad_to_align();
788
789 unsafe {
790 if layout.size() == 0 {
791 #make_zst
792 } else {
793 #alloc
794
795 let fat_ptr = ::core::mem::transmute::<(*mut u8, usize), *mut Self>((mem_ptr, len));
796 ::core::debug_assert_eq!(::core::alloc::Layout::for_value(&*fat_ptr), layout);
797
798 #( #header_field_writes )*
799
800 let tail_ptr = (&raw mut (*fat_ptr).#tail_field).cast::<u8>();
802 ::core::ptr::copy_nonoverlapping(s.as_ptr(), tail_ptr, len);
803
804 #box_path::from_raw(fat_ptr)
805 }
806 }
807 }
808 }
809}
810
811fn factory_for_trait_arg(macro_args: &MacroArgs, struct_info: &StructInfo, trait_path: &Path) -> TokenStream {
812 let alloc = alloc(macro_args.no_std);
813 let box_path = box_path(macro_args.no_std);
814 let make_zst = alloc_zst(&box_path, true);
815
816 let header_layout = header_layout(macro_args, struct_info, true);
817 let tuple_assignment = args_tuple_assignment(struct_info);
818 let header_field_writes = header_field_writes(struct_info);
819 let header_params = header_params(struct_info);
820
821 let factory_name = ¯o_args.base_factory_name;
822 let trait_generic = ¯o_args.generic_name;
823 let visibility = ¯o_args.visibility;
824
825 let struct_name = &struct_info.struct_name;
826 let tail_param = &struct_info.tail_param_ident;
827 let tail_field = &struct_info.tail_field_ident;
828 let tail_args_tuple_idx = Index::from(struct_info.header_fields.len());
829
830 let factory_doc = format!("Builds an instance of `Box<{struct_name}>`.");
831
832 quote! {
833 #[doc = #factory_doc]
834 #[allow(clippy::let_unit_value)]
835 #[allow(clippy::zst_offset)]
836 #[allow(clippy::transmute_undefined_repr)]
837 #visibility fn #factory_name <#trait_generic> (
838 #( #header_params, )*
839 #tail_param: #trait_generic
840 ) -> #box_path<Self>
841 where
842 #trait_generic: #trait_path + Sized
843 {
844 #tuple_assignment
845
846 let s = args.#tail_args_tuple_idx;
847 let trait_object: &dyn #trait_path = &s;
848 let (_, vtable): (*const #trait_generic, *const ()) = unsafe { ::core::mem::transmute(trait_object) };
849
850 #header_layout
851 let layout = layout.extend(::core::alloc::Layout::new::<#trait_generic>()).expect("Struct exceeds maximum size allowed of isize::MAX").0;
852 let layout = layout.pad_to_align();
853
854 unsafe {
855 if layout.size() == 0 {
856 #make_zst
857 } else {
858 #alloc
859
860 let fat_ptr = ::core::mem::transmute::<(*mut u8, *const ()), *mut Self>((mem_ptr, vtable));
861 ::core::debug_assert_eq!(::core::alloc::Layout::for_value(&*fat_ptr), layout);
862
863 #( #header_field_writes )*
864
865 let tail_ptr = (&raw mut (*fat_ptr).#tail_field).cast::<#trait_generic>();
866 ::core::ptr::copy_nonoverlapping(::core::ptr::addr_of!(s), tail_ptr, 1);
867
868 #box_path::from_raw(fat_ptr)
869 }
870 }
871 }
872 }
873}
874
875fn make_dst_factory_impl(attr_args: TokenStream, item: TokenStream) -> SynResult<TokenStream> {
876 let macro_args = MacroArgs::parse(attr_args)?;
877 let input_struct: ItemStruct = syn::parse2(item)?;
878 let struct_info = StructInfo::new(&input_struct)?;
879
880 let mut factories = Vec::new();
881 let mut iterator_type = None;
882 match &struct_info.tail_kind {
883 TailKind::Slice(elem_type) => {
884 factories.push(factory_for_iter_arg(¯o_args, &struct_info, elem_type));
885 factories.push(factory_for_slice_arg(¯o_args, &struct_info, elem_type));
886 factories.push(destructurer_with_iter(¯o_args, &struct_info));
887 iterator_type = Some(destructurer_iterator_type(¯o_args, &struct_info, elem_type));
888 }
889
890 TailKind::Str => {
891 factories.push(factory_for_str_arg(¯o_args, &struct_info));
892 }
893
894 TailKind::TraitObject(trait_path) => {
895 factories.push(factory_for_trait_arg(¯o_args, &struct_info, trait_path));
896 }
897 }
898
899 let (impl_generics, ty_generics, where_clause) = struct_info.struct_generics.split_for_impl();
900 let struct_name_ident = struct_info.struct_name;
901
902 Ok(quote! {
903 #input_struct
904
905 impl #impl_generics #struct_name_ident #ty_generics #where_clause {
906 #( #factories )*
907 }
908
909 #iterator_type
910 })
911}
912
913#[proc_macro_attribute]
955pub fn make_dst_factory(attr_args: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
956 let result = make_dst_factory_impl(attr_args.into(), item.into());
957 result.unwrap_or_else(|err| err.to_compile_error()).into()
958}