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 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 = 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 #[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}