ptr_meta_derive/
lib.rs

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 = 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/// Generates an implementation of `Pointee` for trait objects.
56#[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}