1 use proc_macro2::TokenStream;
2 use quote::quote;
3 use 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)]
pointee_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream8 pub fn pointee_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9     let input = parse_macro_input!(input as DeriveInput);
10 
11     proc_macro::TokenStream::from(derive_pointee_impl(&input))
12 }
13 
derive_pointee_impl(input: &DeriveInput) -> TokenStream14 fn 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]
pointee( _attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream73 pub fn pointee(
74     _attr: proc_macro::TokenStream,
75     item: proc_macro::TokenStream,
76 ) -> proc_macro::TokenStream {
77     let input = parse_macro_input!(item as ItemTrait);
78 
79     let ident = &input.ident;
80 
81     let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
82 
83     let result = 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