bointer_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro2::{TokenStream, TokenTree};
4use quote::quote;
5use venial::{parse_declaration, Declaration, Error, StructFields};
6
7#[proc_macro_derive(ReprTransparent)]
8pub fn derive_repr_transparent(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9    parse_declaration(input.into())
10        .and_then(derive)
11        .unwrap_or_else(|e| e.to_compile_error())
12        .into()
13}
14
15fn derive(decl: Declaration) -> Result<TokenStream, Error> {
16    let Some(repr_attribute) = decl.attributes().iter().find(|attr| {
17        attr.get_single_path_segment()
18            .map(|ident| ident == "repr")
19            .unwrap_or(false)
20    }) else {
21        return Err(Error::new_at_tokens(
22            decl,
23            "This derive only supports types with `#[repr(...)]`",
24        ));
25    };
26
27    if !matches!(
28        repr_attribute.get_value_tokens(),
29        [TokenTree::Ident(id)] if id == "transparent"
30    ) {
31        return Err(Error::new_at_tokens(
32            repr_attribute,
33            "`#[repr(transparent)]` is required to implement `ReprTransparent`",
34        ));
35    }
36
37    let (inner_type, inline_generic_args, name, generic_params, where_clause) = match &decl {
38        Declaration::Struct(decl) => {
39            let inner_type =
40                decl.field_types().into_iter().next().ok_or_else(|| {
41                    Error::new_at_tokens(decl, "Struct must have at least one field")
42                })?;
43
44            (
45                inner_type,
46                decl.get_inline_generic_args(),
47                &decl.name,
48                &decl.generic_params,
49                &decl.where_clause,
50            )
51        }
52        Declaration::Enum(decl) => {
53            let (variant, _) = decl
54                .variants
55                .first()
56                .ok_or_else(|| Error::new_at_tokens(decl, "Enum must have one variant"))?;
57
58            let inner_type = match &variant.contents {
59                StructFields::Unit => {
60                    return Err(Error::new_at_tokens(
61                        variant,
62                        "Enum variant must have at least one field",
63                    ))
64                }
65                StructFields::Tuple(f) => f.fields.first().map(|f| &f.0.ty).ok_or_else(|| {
66                    Error::new_at_tokens(variant, "Enum variant must have at least one field")
67                })?,
68                StructFields::Named(f) => f.fields.first().map(|f| &f.0.ty).ok_or_else(|| {
69                    Error::new_at_tokens(variant, "Enum variant must have at least one field")
70                })?,
71            };
72
73            (
74                inner_type,
75                decl.get_inline_generic_args(),
76                &decl.name,
77                &decl.generic_params,
78                &decl.where_clause,
79            )
80        }
81        _ => return Err(Error::new("This derive only supports structs or enums")),
82    };
83
84    // Build the output, possibly using quasi-quotation
85    Ok(quote! {
86        unsafe impl #inline_generic_args bointer::ReprTransparent for #name #generic_params #where_clause {
87            type Inner = #inner_type;
88
89            #[inline(always)]
90            fn into_inner(self) -> Self::Inner {
91                // SAFETY: Safe for the conditions described in the trait documentation.
92                unsafe { core::mem::transmute(self) }
93            }
94        }
95    })
96}