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)] |
8 | pub 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 | |
14 | 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 ] |
73 | pub 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 | |