rune_macros/
module.rs

1use proc_macro2::{Span, TokenStream};
2use quote::{quote, ToTokens};
3use syn::parse::ParseStream;
4use syn::punctuated::Punctuated;
5
6pub(crate) struct ModuleAttrs {
7    path: syn::Path,
8}
9
10impl ModuleAttrs {
11    /// Parse the given parse stream.
12    pub(crate) fn parse(input: ParseStream) -> syn::Result<Self> {
13        let path = input.parse::<syn::Path>()?;
14        let stream = input.parse::<TokenStream>()?;
15
16        if !stream.is_empty() {
17            return Err(syn::Error::new_spanned(stream, "Unexpected input"));
18        }
19
20        Ok(Self { path })
21    }
22}
23
24pub(crate) struct Module {
25    attributes: Vec<syn::Attribute>,
26    docs: syn::ExprArray,
27    vis: syn::Visibility,
28    signature: syn::Signature,
29    remainder: TokenStream,
30}
31
32impl Module {
33    /// Parse the given parse stream.
34    pub(crate) fn parse(input: ParseStream) -> syn::Result<Self> {
35        let parsed_attributes = input.call(syn::Attribute::parse_outer)?;
36
37        let mut docs = syn::ExprArray {
38            attrs: Vec::new(),
39            bracket_token: syn::token::Bracket::default(),
40            elems: Punctuated::default(),
41        };
42
43        let mut attributes = Vec::new();
44
45        for attr in parsed_attributes {
46            if attr.path().is_ident("doc") {
47                if let syn::Meta::NameValue(name_value) = &attr.meta {
48                    docs.elems.push(name_value.value.clone());
49                }
50            }
51
52            attributes.push(attr);
53        }
54
55        Ok(Self {
56            attributes,
57            docs,
58            vis: input.parse()?,
59            signature: input.parse()?,
60            remainder: input.parse()?,
61        })
62    }
63
64    /// Expand the function declaration.
65    pub(crate) fn expand(self, attrs: ModuleAttrs) -> syn::Result<TokenStream> {
66        let docs = self.docs;
67
68        let item_buf = crate::item::build_buf(&attrs.path)?;
69        let item_bytes = crate::item::buf_as_bytes(&item_buf);
70
71        let mut stream = TokenStream::new();
72
73        let name = quote::format_ident!("{}__meta", self.signature.ident);
74        let doc = syn::LitStr::new(
75            &format!(" Module metadata for `{item_buf}`."),
76            Span::call_site(),
77        );
78
79        stream.extend(quote! {
80            #[doc = #doc]
81            #[automatically_derived]
82            #[allow(non_snake_case)]
83            #[doc(hidden)]
84            fn #name() -> Result<rune::__priv::ModuleMetaData, rune::alloc::Error> {
85                Ok(rune::__priv::ModuleMetaData {
86                    item: unsafe { rune::__priv::Item::from_bytes(&#item_bytes) },
87                    docs: &#docs[..],
88                })
89            }
90        });
91
92        stream.extend(quote!(#[allow(rustdoc::broken_intra_doc_links)]));
93
94        for attribute in self.attributes {
95            attribute.to_tokens(&mut stream);
96        }
97
98        self.vis.to_tokens(&mut stream);
99        self.signature.to_tokens(&mut stream);
100        stream.extend(self.remainder);
101        Ok(stream)
102    }
103}