1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput, Error, Fields, ItemTrait};
4
5#[proc_macro_derive(Pointee)]
8pub 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
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(ident.span(), "dynamically sized structs must contain at least one field").to_compile_error();
24 }
25 },
26 Fields::Unnamed(ref fields) => {
27 if let Some(result) = fields.unnamed.last() {
28 &result.ty
29 } else {
30 return Error::new(ident.span(), "dynamically sized structs must contain at least one field").to_compile_error();
31 }
32 },
33 Fields::Unit => return Error::new(ident.span(), "unit structs cannot be dynamically sized").to_compile_error(),
34 },
35 Data::Enum(_) => return Error::new(ident.span(), "enums cannot be dynamically sized").to_compile_error(),
36 Data::Union(_) => return Error::new(ident.span(), "unions cannot be dynamically sized").to_compile_error(),
37 };
38
39 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
40
41 quote! {
42 const _: () = {
43 use ptr_meta::Pointee;
44
45 impl #impl_generics Pointee for #ident #ty_generics #where_clause
46 where
47 #last_field_ty: Pointee,
48 {
49 type Metadata = <#last_field_ty as Pointee>::Metadata;
50 }
51 };
52 }
53}
54
55#[proc_macro_attribute]
57pub fn pointee(
58 _attr: proc_macro::TokenStream,
59 item: proc_macro::TokenStream,
60) -> proc_macro::TokenStream {
61 let input = parse_macro_input!(item as ItemTrait);
62
63 let ident = &input.ident;
64
65 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
66
67 let result = quote! {
68 #input
69
70 const _: () = {
71 use ptr_meta::{DynMetadata, Pointee};
72
73 impl #impl_generics Pointee for (dyn #ident #ty_generics #where_clause + '_) {
74 type Metadata = DynMetadata<Self>;
75 }
76 };
77 };
78
79 proc_macro::TokenStream::from(result)
80}