rune_macros/
lib.rs

1//! <img alt="rune logo" src="https://raw.githubusercontent.com/rune-rs/rune/main/assets/icon.png" />
2//! <br>
3//! <a href="https://github.com/rune-rs/rune"><img alt="github" src="https://img.shields.io/badge/github-rune--rs/rune-8da0cb?style=for-the-badge&logo=github" height="20"></a>
4//! <a href="https://crates.io/crates/rune-macros"><img alt="crates.io" src="https://img.shields.io/crates/v/rune-macros.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20"></a>
5//! <a href="https://docs.rs/rune-macros"><img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-rune--macros-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20"></a>
6//! <a href="https://discord.gg/v5AeNkT"><img alt="chat on discord" src="https://img.shields.io/discord/558644981137670144.svg?logo=discord&style=flat-square" height="20"></a>
7//! <br>
8//! Minimum support: Rust <b>1.81+</b>.
9//! <br>
10//! <br>
11//! <a href="https://rune-rs.github.io"><b>Visit the site 🌐</b></a>
12//! &mdash;
13//! <a href="https://rune-rs.github.io/book/"><b>Read the book 📖</b></a>
14//! <br>
15//! <br>
16//!
17//! Macros for the Rune Language, an embeddable dynamic programming language for Rust.
18//!
19//! <br>
20//!
21//! ## Usage
22//!
23//! This is part of the [Rune Language](https://rune-rs.github.io).
24
25#![allow(clippy::manual_map)]
26#![allow(clippy::too_many_arguments)]
27
28mod any;
29mod const_value;
30mod context;
31mod from_value;
32mod function;
33mod hash;
34mod inst_display;
35mod item;
36mod macro_;
37mod module;
38mod opaque;
39mod parse;
40mod path_in;
41mod quote;
42mod spanned;
43mod to_tokens;
44mod to_value;
45
46use self::context::{Context, Tokens};
47
48use ::quote::format_ident;
49use proc_macro2::TokenStream;
50use syn::{Generics, Path};
51
52const RUNE: &str = "rune";
53
54#[proc_macro]
55pub fn quote(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
56    let input = proc_macro2::TokenStream::from(input);
57    let parser = crate::quote::Quote::new();
58
59    let output = match parser.parse(input) {
60        Ok(output) => output,
61        Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
62    };
63
64    output.into()
65}
66
67#[proc_macro_attribute]
68pub fn function(
69    attrs: proc_macro::TokenStream,
70    item: proc_macro::TokenStream,
71) -> proc_macro::TokenStream {
72    let attrs = syn::parse_macro_input!(attrs with crate::function::FunctionAttrs::parse);
73    let function = syn::parse_macro_input!(item with crate::function::Function::parse);
74
75    let output = match function.expand(attrs) {
76        Ok(output) => output,
77        Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
78    };
79
80    output.into()
81}
82
83#[proc_macro_attribute]
84pub fn macro_(
85    attrs: proc_macro::TokenStream,
86    item: proc_macro::TokenStream,
87) -> proc_macro::TokenStream {
88    let attrs = syn::parse_macro_input!(attrs with crate::macro_::Config::parse);
89    let macro_ = syn::parse_macro_input!(item with crate::macro_::Macro::parse);
90
91    let output = match macro_.expand(attrs, format_ident!("function")) {
92        Ok(output) => output,
93        Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
94    };
95
96    output.into()
97}
98
99#[proc_macro_attribute]
100pub fn module(
101    attrs: proc_macro::TokenStream,
102    item: proc_macro::TokenStream,
103) -> proc_macro::TokenStream {
104    let attrs = syn::parse_macro_input!(attrs with crate::module::ModuleAttrs::parse);
105    let module = syn::parse_macro_input!(item with crate::module::Module::parse);
106
107    let output = match module.expand(attrs) {
108        Ok(output) => output,
109        Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
110    };
111
112    output.into()
113}
114
115#[proc_macro_attribute]
116pub fn attribute_macro(
117    attrs: proc_macro::TokenStream,
118    item: proc_macro::TokenStream,
119) -> proc_macro::TokenStream {
120    let attrs = syn::parse_macro_input!(attrs with crate::macro_::Config::parse);
121    let macro_ = syn::parse_macro_input!(item with crate::macro_::Macro::parse);
122
123    let output = match macro_.expand(attrs, format_ident!("attribute")) {
124        Ok(output) => output,
125        Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
126    };
127
128    output.into()
129}
130
131#[proc_macro_derive(ToTokens, attributes(rune))]
132#[doc(hidden)]
133pub fn to_tokens(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
134    let derive = syn::parse_macro_input!(input as to_tokens::Derive);
135    Context::build(|cx| derive.expand(cx)).into()
136}
137
138#[proc_macro_derive(Parse, attributes(rune))]
139#[doc(hidden)]
140pub fn parse(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
141    let derive = syn::parse_macro_input!(input as parse::Derive);
142    Context::build(|cx| derive.expand(cx)).into()
143}
144
145#[proc_macro_derive(Spanned, attributes(rune))]
146#[doc(hidden)]
147pub fn spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
148    let derive = syn::parse_macro_input!(input as spanned::Derive);
149    Context::build(|cx| derive.expand(cx, false)).into()
150}
151
152#[proc_macro_derive(OptionSpanned, attributes(rune))]
153#[doc(hidden)]
154pub fn option_spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
155    let derive = syn::parse_macro_input!(input as spanned::Derive);
156    Context::build(|cx| derive.expand(cx, true)).into()
157}
158
159#[proc_macro_derive(Opaque, attributes(rune))]
160#[doc(hidden)]
161pub fn opaque(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
162    let derive = syn::parse_macro_input!(input as opaque::Derive);
163    Context::build(|cx| derive.expand(cx)).into()
164}
165
166#[proc_macro_derive(FromValue, attributes(rune))]
167pub fn from_value(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
168    let input = syn::parse_macro_input!(input as syn::DeriveInput);
169    Context::build(|cx| from_value::expand(cx, &input)).into()
170}
171
172#[proc_macro_derive(ToValue, attributes(rune))]
173pub fn to_value(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
174    let input = syn::parse_macro_input!(input as syn::DeriveInput);
175    Context::build(|cx| to_value::expand(cx, &input)).into()
176}
177
178#[proc_macro_derive(Any, attributes(rune))]
179pub fn any(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
180    let derive = syn::parse_macro_input!(input as any::Derive);
181
182    let stream = Context::build(|cx| {
183        let attr = cx.type_attrs(&derive.input.attrs);
184        let tokens = cx.tokens_with_module(attr.module.as_ref());
185        Ok(derive.into_any_builder(cx, &attr, &tokens)?.expand())
186    });
187
188    stream.into()
189}
190
191#[proc_macro_derive(ToConstValue, attributes(const_value))]
192pub fn const_value(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
193    let derive = syn::parse_macro_input!(input as const_value::Derive);
194    Context::build(|cx| Ok(derive.into_builder(cx)?.expand())).into()
195}
196
197/// Calculate a type hash at compile time.
198///
199/// # Examples
200///
201/// ```
202/// use rune::Hash;
203///
204/// let hash: Hash = rune::hash!(::std::ops::generator::Generator);
205/// ```
206#[proc_macro]
207pub fn hash(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
208    let args = syn::parse_macro_input!(input as self::hash::Arguments);
209
210    let stream = Context::build(|cx| {
211        let Tokens { hash, .. } = cx.tokens_with_module(None);
212        let value = args.build_type_hash(cx)?.into_inner();
213        Ok(::quote::quote!(#hash(#value)))
214    });
215
216    stream.into()
217}
218
219#[doc(hidden)]
220#[proc_macro]
221pub fn hash_in(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
222    let path_in::PathIn { in_crate, item, .. } =
223        syn::parse_macro_input!(input as path_in::PathIn<self::hash::Arguments>);
224
225    let stream = Context::build(|cx| {
226        let value = item.build_type_hash(cx)?.into_inner();
227        Ok(::quote::quote!(#in_crate::Hash(#value)))
228    });
229
230    stream.into()
231}
232
233/// Calculate an item reference at compile time.
234///
235/// # Examples
236///
237/// ```
238/// use rune::{Item, ItemBuf};
239///
240/// static ITEM: &Item = rune::item!(::std::ops::generator::Generator);
241///
242/// let mut item = ItemBuf::with_crate("std")?;
243/// item.push("ops")?;
244/// item.push("generator")?;
245/// item.push("Generator")?;
246///
247/// assert_eq!(item, ITEM);
248/// # Ok::<_, rune::alloc::Error>(())
249/// ```
250#[proc_macro]
251pub fn item(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
252    let path = syn::parse_macro_input!(input as syn::Path);
253
254    let stream = match self::item::build_item(&path) {
255        Ok(hash) => {
256            ::quote::quote!(unsafe { rune::Item::from_bytes(&#hash) })
257        }
258        Err(error) => to_compile_errors([error]),
259    };
260
261    stream.into()
262}
263
264#[proc_macro]
265#[doc(hidden)]
266pub fn item_in(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
267    let path_in::PathIn { in_crate, item, .. } = syn::parse_macro_input!(input as path_in::PathIn);
268
269    let stream = match self::item::build_item(&item) {
270        Ok(hash) => {
271            ::quote::quote!(unsafe { #in_crate::Item::from_bytes(&#hash) })
272        }
273        Err(error) => to_compile_errors([error]),
274    };
275
276    stream.into()
277}
278
279/// Helper to generate bindings for an external type.
280///
281/// This can *only* be used inside of the `rune` crate itself.
282#[proc_macro]
283#[doc(hidden)]
284pub fn binding(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
285    let derive = syn::parse_macro_input!(input as any::InternalCall);
286
287    let stream = Context::build_with_crate(|cx| {
288        let mut stream = TokenStream::default();
289        let attr = context::TypeAttr::default();
290        let tokens = cx.tokens_with_module(None);
291
292        for builder in derive.into_any_builders(cx, &attr, &tokens) {
293            stream.extend(builder.expand());
294        }
295
296        Ok(stream)
297    });
298
299    stream.into()
300}
301
302/// Shim for an ignored `#[stable]` attribute.
303#[proc_macro_attribute]
304#[doc(hidden)]
305pub fn stable(
306    _attr: proc_macro::TokenStream,
307    item: proc_macro::TokenStream,
308) -> proc_macro::TokenStream {
309    item
310}
311
312/// Shim for an ignored `#[unstable]` attribute.
313#[proc_macro_attribute]
314#[doc(hidden)]
315pub fn unstable(
316    _attr: proc_macro::TokenStream,
317    item: proc_macro::TokenStream,
318) -> proc_macro::TokenStream {
319    item
320}
321
322#[proc_macro_derive(InstDisplay, attributes(inst_display))]
323#[doc(hidden)]
324pub fn inst_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
325    let derive = syn::parse_macro_input!(input as inst_display::Derive);
326    derive.expand().unwrap_or_else(to_compile_errors).into()
327}
328
329/// Adds the `path` as trait bound to each generic
330fn add_trait_bounds(generics: &mut Generics, path: &Path) {
331    for ty in &mut generics.type_params_mut() {
332        ty.bounds.push(syn::TypeParamBound::Trait(syn::TraitBound {
333            paren_token: None,
334            modifier: syn::TraitBoundModifier::None,
335            lifetimes: None,
336            path: path.clone(),
337        }));
338    }
339}
340
341fn to_compile_errors<I>(errors: I) -> proc_macro2::TokenStream
342where
343    I: IntoIterator<Item = syn::Error>,
344{
345    let compile_errors = errors.into_iter().map(syn::Error::into_compile_error);
346    ::quote::quote!(#(#compile_errors)*)
347}