rune_macros/
opaque.rs
1use crate::context::{Context, Tokens};
2use proc_macro2::TokenStream;
3use quote::quote;
4
5pub struct Derive {
7 input: syn::DeriveInput,
8}
9
10impl syn::parse::Parse for Derive {
11 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
12 Ok(Self {
13 input: input.parse()?,
14 })
15 }
16}
17
18impl Derive {
19 pub(super) fn expand(self, cx: &Context) -> Result<TokenStream, ()> {
20 let attr = cx.type_attrs(&self.input.attrs);
21 let tokens = cx.tokens_with_module(attr.module.as_ref());
22 let mut expander = Expander { cx, tokens };
23
24 match &self.input.data {
25 syn::Data::Struct(st) => Ok(expander.expand_struct(&self.input, st)?),
26 syn::Data::Enum(en) => {
27 expander.cx.error(syn::Error::new_spanned(
28 en.enum_token,
29 "not supported on enums",
30 ));
31
32 Err(())
33 }
34 syn::Data::Union(un) => {
35 expander.cx.error(syn::Error::new_spanned(
36 un.union_token,
37 "not supported on unions",
38 ));
39
40 Err(())
41 }
42 }
43 }
44}
45
46struct Expander<'cx> {
47 cx: &'cx Context,
48 tokens: Tokens,
49}
50
51impl Expander<'_> {
52 fn expand_struct(
54 &mut self,
55 input: &syn::DeriveInput,
56 st: &syn::DataStruct,
57 ) -> Result<TokenStream, ()> {
58 let accessor = self.pick_field(&st.fields)?;
59
60 let ident = &input.ident;
61 let opaque = &self.tokens.opaque;
62 let id = &self.tokens.id;
63
64 let (gen_impl, gen_type, gen_where) = input.generics.split_for_impl();
65
66 Ok(quote! {
67 #[automatically_derived]
68 impl #gen_impl #opaque for #ident #gen_type #gen_where {
69 fn id(&self) -> #id {
70 #accessor
71 }
72 }
73 })
74 }
75
76 fn pick_field(&mut self, fields: &syn::Fields) -> Result<TokenStream, ()> {
78 let mut field = None;
79
80 for (n, f) in fields.iter().enumerate() {
81 let attrs = self.cx.field_attrs(&f.attrs);
82
83 if attrs.id.is_some() {
84 if field.is_some() {
85 self.cx.error(syn::Error::new_spanned(
86 f,
87 "only one field can be marked `#[rune(id)]`",
88 ));
89 }
90
91 field = Some((n, f));
92 }
93 }
94
95 let Some((n, f)) = field else {
96 self.cx.error(syn::Error::new_spanned(
97 fields,
98 "Could not find a suitable identifier field",
99 ));
100 return Err(());
101 };
102
103 Ok(match &f.ident {
104 Some(ident) => quote!(self.#ident),
105 None => quote!(self.#n),
106 })
107 }
108}