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 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 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 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}