1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput, Error, Fields, ItemTrait};
4
5/// Generates an implementation of `Pointee` for structs with a DST as its last
6/// field.
7#[proc_macro_derive(Pointee)]
8pub fn pointee_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9 let input: DeriveInput = parse_macro_input!(input as DeriveInput);
10
11 proc_macro::TokenStream::from(derive_pointee_impl(&input))
12}
13
14fn derive_pointee_impl(input: &DeriveInput) -> TokenStream {
15 let ident = &input.ident;
16
17 let last_field_ty = match input.data {
18 Data::Struct(ref data) => match data.fields {
19 Fields::Named(ref fields) => {
20 if let Some(result) = fields.named.last() {
21 &result.ty
22 } else {
23 return Error::new(
24 ident.span(),
25 "dynamically sized structs must contain at least one field",
26 )
27 .to_compile_error();
28 }
29 }
30 Fields::Unnamed(ref fields) => {
31 if let Some(result) = fields.unnamed.last() {
32 &result.ty
33 } else {
34 return Error::new(
35 ident.span(),
36 "dynamically sized structs must contain at least one field",
37 )
38 .to_compile_error();
39 }
40 }
41 Fields::Unit => {
42 return Error::new(ident.span(), "unit structs cannot be dynamically sized")
43 .to_compile_error()
44 }
45 },
46 Data::Enum(_) => {
47 return Error::new(ident.span(), "enums cannot be dynamically sized").to_compile_error()
48 }
49 Data::Union(_) => {
50 return Error::new(ident.span(), "unions cannot be dynamically sized")
51 .to_compile_error()
52 }
53 };
54
55 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
56
57 quote! {
58 const _: () = {
59 use ptr_meta::Pointee;
60
61 impl #impl_generics Pointee for #ident #ty_generics #where_clause
62 where
63 #last_field_ty: Pointee,
64 {
65 type Metadata = <#last_field_ty as Pointee>::Metadata;
66 }
67 };
68 }
69}
70
71/// Generates an implementation of `Pointee` for trait objects.
72#[proc_macro_attribute]
73pub fn pointee(
74 _attr: proc_macro::TokenStream,
75 item: proc_macro::TokenStream,
76) -> proc_macro::TokenStream {
77 let input: ItemTrait = parse_macro_input!(item as ItemTrait);
78
79 let ident: &Ident = &input.ident;
80
81 let (impl_generics: ImplGenerics<'_>, ty_generics: TypeGenerics<'_>, where_clause: Option<&WhereClause>) = input.generics.split_for_impl();
82
83 let result: TokenStream = quote! {
84 #input
85
86 const _: () = {
87 use ptr_meta::{DynMetadata, Pointee};
88
89 impl #impl_generics Pointee for (dyn #ident #ty_generics #where_clause + '_) {
90 type Metadata = DynMetadata<Self>;
91 }
92 };
93 };
94
95 proc_macro::TokenStream::from(result)
96}
97