rune_macros/
module.rs

1use proc_macro2::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 = match attrs.path.leading_colon {
69            Some(..) => {
70                let mut it = attrs.path.segments.iter();
71
72                let Some(krate) = it.next() else {
73                    return Err(syn::Error::new_spanned(
74                        &attrs.path,
75                        "missing leading segment",
76                    ));
77                };
78
79                let krate = syn::LitStr::new(&krate.ident.to_string(), krate.ident.span());
80                let item = build_item(it);
81
82                if item.elems.is_empty() {
83                    quote!(rune::__private::ItemBuf::with_crate(#krate)?)
84                } else {
85                    quote!(rune::__private::ItemBuf::with_crate_item(#krate, #item)?)
86                }
87            }
88            None => {
89                let item = build_item(attrs.path.segments.iter());
90
91                if item.elems.is_empty() {
92                    quote!(rune::__private::ItemBuf::new()?)
93                } else {
94                    quote!(rune::__private::ItemBuf::from_item(#item)?)
95                }
96            }
97        };
98
99        let mut stream = TokenStream::new();
100
101        stream.extend(quote! {
102            /// Get module metadata.
103            #[automatically_derived]
104            #[doc(hidden)]
105            fn module_meta() -> rune::alloc::Result<rune::__private::ModuleMetaData> {
106                Ok(rune::__private::ModuleMetaData {
107                    docs: &#docs[..],
108                    item: #item,
109                })
110            }
111        });
112
113        stream.extend(quote!(#[allow(rustdoc::broken_intra_doc_links)]));
114
115        for attribute in self.attributes {
116            attribute.to_tokens(&mut stream);
117        }
118
119        self.vis.to_tokens(&mut stream);
120        self.signature.to_tokens(&mut stream);
121        stream.extend(self.remainder);
122        Ok(stream)
123    }
124}
125
126fn build_item(it: syn::punctuated::Iter<'_, syn::PathSegment>) -> syn::ExprArray {
127    let mut item = syn::ExprArray {
128        attrs: Vec::new(),
129        bracket_token: syn::token::Bracket::default(),
130        elems: Punctuated::default(),
131    };
132
133    for p in it {
134        let p = syn::LitStr::new(&p.ident.to_string(), p.ident.span());
135
136        item.elems.push(syn::Expr::Lit(syn::ExprLit {
137            attrs: Vec::new(),
138            lit: syn::Lit::Str(p),
139        }))
140    }
141    item
142}