rune_macros/
macro_.rs
1use proc_macro2::{Ident, TokenStream};
2use quote::{quote, ToTokens};
3use syn::parse::ParseStream;
4use syn::punctuated::Punctuated;
5use syn::spanned::Spanned;
6
7#[derive(Default)]
8enum Path {
9 #[default]
10 None,
11 Path(syn::Path),
12}
13
14#[derive(Default)]
15pub(crate) struct Config {
16 path: Path,
17}
18
19impl Config {
20 pub(crate) fn parse(input: ParseStream) -> syn::Result<Self> {
22 let mut out = Self::default();
23
24 while !input.is_empty() {
25 let ident = input.parse::<syn::Ident>()?;
26
27 if ident != "path" {
28 return Err(syn::Error::new_spanned(ident, "Unsupported option"));
29 }
30
31 input.parse::<syn::Token![=]>()?;
32 out.path = Path::Path(input.parse()?);
33
34 if input.parse::<Option<syn::Token![,]>>()?.is_none() {
35 break;
36 }
37 }
38
39 let stream = input.parse::<TokenStream>()?;
40
41 if !stream.is_empty() {
42 return Err(syn::Error::new_spanned(stream, "Unexpected input"));
43 }
44
45 Ok(out)
46 }
47}
48
49pub(crate) struct Macro {
50 attributes: Vec<syn::Attribute>,
51 vis: syn::Visibility,
52 sig: syn::Signature,
53 remainder: TokenStream,
54 name_string: syn::LitStr,
55 docs: syn::ExprArray,
56 meta_vis: syn::Visibility,
57 real_fn: syn::Ident,
58 meta_fn: syn::Ident,
59}
60
61impl Macro {
62 pub(crate) fn parse(input: ParseStream) -> syn::Result<Self> {
64 let parsed_attributes = input.call(syn::Attribute::parse_outer)?;
65 let vis = input.parse::<syn::Visibility>()?;
66 let mut sig = input.parse::<syn::Signature>()?;
67 let ident = sig.ident.clone();
68
69 let mut attributes = Vec::new();
70
71 let mut docs = syn::ExprArray {
72 attrs: Vec::new(),
73 bracket_token: syn::token::Bracket::default(),
74 elems: Punctuated::default(),
75 };
76
77 for attr in parsed_attributes {
78 if attr.path().is_ident("doc") {
79 if let syn::Meta::NameValue(name_value) = &attr.meta {
80 docs.elems.push(name_value.value.clone());
81 }
82 }
83
84 attributes.push(attr);
85 }
86
87 let name_string = syn::LitStr::new(&ident.to_string(), ident.span());
88
89 let meta_vis = vis.clone();
90 let meta_fn = sig.ident.clone();
91 let real_fn = syn::Ident::new(&format!("__rune_macro__{}", sig.ident), sig.ident.span());
92 sig.ident = real_fn.clone();
93
94 let remainder = input.parse::<TokenStream>()?;
95
96 Ok(Self {
97 attributes,
98 vis,
99 sig,
100 remainder,
101 name_string,
102 docs,
103 meta_vis,
104 real_fn,
105 meta_fn,
106 })
107 }
108
109 pub(crate) fn expand(self, attrs: Config, macro_kind: Ident) -> syn::Result<TokenStream> {
111 let real_fn_path = {
112 let mut segments = Punctuated::default();
113
114 segments.push(syn::PathSegment {
115 ident: self.real_fn,
116 arguments: syn::PathArguments::None,
117 });
118
119 syn::TypePath {
120 qself: None,
121 path: syn::Path {
122 leading_colon: None,
123 segments,
124 },
125 }
126 };
127
128 let meta_name = syn::Expr::Array({
129 let mut meta_name = syn::ExprArray {
130 attrs: Vec::new(),
131 bracket_token: syn::token::Bracket::default(),
132 elems: Punctuated::default(),
133 };
134
135 match attrs.path {
136 Path::None => {
137 meta_name.elems.push(syn::Expr::Lit(syn::ExprLit {
138 attrs: Vec::new(),
139 lit: syn::Lit::Str(self.name_string.clone()),
140 }));
141 }
142 Path::Path(path) => {
143 for s in &path.segments {
144 let syn::PathArguments::None = s.arguments else {
145 return Err(syn::Error::new_spanned(
146 s,
147 "Expected simple ident path segment",
148 ));
149 };
150
151 let ident = syn::LitStr::new(&s.ident.to_string(), s.span());
152
153 meta_name.elems.push(syn::Expr::Lit(syn::ExprLit {
154 attrs: Vec::new(),
155 lit: syn::Lit::Str(ident),
156 }));
157 }
158 }
159 }
160
161 meta_name
162 });
163
164 let mut stream = TokenStream::new();
165
166 for attr in self.attributes {
167 stream.extend(attr.into_token_stream());
168 }
169
170 stream.extend(quote!(#[allow(non_snake_case)]));
171 stream.extend(self.vis.into_token_stream());
172 stream.extend(self.sig.into_token_stream());
173 stream.extend(self.remainder);
174
175 let meta_vis = &self.meta_vis;
176 let meta_fn = &self.meta_fn;
177 let docs = &self.docs;
178 let name_string = self.name_string;
179
180 stream.extend(quote! {
181 #[automatically_derived]
183 #meta_vis fn #meta_fn() -> rune::alloc::Result<rune::__private::MacroMetaData> {
184 Ok(rune::__private::MacroMetaData {
185 kind: rune::__private::MacroMetaKind::#macro_kind(#meta_name, #real_fn_path)?,
186 name: #name_string,
187 docs: &#docs[..],
188 })
189 }
190 });
191
192 Ok(stream)
193 }
194}