rune_macros/
opaque.rs

1use crate::context::{Context, Tokens};
2use proc_macro2::TokenStream;
3use quote::quote;
4
5/// Derive implementation of `Opaque`.
6pub 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    /// Expand on a struct.
53    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    /// Expand field decoding.
77    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}