mini_macro_magic/lib.rs
1#![no_std]
2#![forbid(unsafe_code)]
3//! Export tokens to other modules or crates. Now with 100% less proc macros!
4//!
5//! See the [integration tests](https://gitlab.com/konnorandrews/mini-macro-magic/-/tree/main/tests) for examples
6//! that show *why* you would want to use [`export`].
7//!
8//! This crate provides the [`export`] macro which allows exporting tokens.
9//! The concept is similar to that used by the inspiration for this crate [`macro_magic`](https://docs.rs/macro_magic/latest/macro_magic/).
10//! Namely, a `macro_rules!` is generated. This `macro_rules!` will invoke a passed macro with the exported tokens.
11//!
12//! The difference to `macro_magic` is that this
13//! crate does it all with one `macro_rules!` macro. No more need to poll in a set of proc macros
14//! if you don't need the full power of `macro_magic`. Instead use [`export`] to generate a
15//! macro you or a dependant crate can use. Also, a dependant crate doesn't need to know about
16//! [`mini_macro_magic`][self] to use the generated `macro_rules!`. They are fully self contained.
17//!
18//! The [`export`] macro allows for a form of reflection. Reflection over the definition
19//! of items by inspecting the tokens of the definition. `macro_rules` (and Rust macros
20//! in general) have no way to eagerly expand their input. As a result [`export`] creates
21//! a macro that you pass another macro call for it to expand into.
22//!
23//! ```
24//! use mini_macro_magic::{export, emit};
25//!
26//! // Export the definition of `MyStruct`.
27//! // Note without using `emit!()` `MyStruct` isn't actually created in this scope.
28//! export!(
29//! #[export(my_struct$)]
30//! {
31//! struct MyStruct;
32//! }
33//! );
34//!
35//! // An example macro that can parse a struct definition and get the name
36//! // of the struct as a string.
37//! macro_rules! name_of_struct {
38//! {{
39//! $(#[$($attr:tt)*])*
40//! $vis:vis struct $name:ident;
41//! }} => {
42//! stringify!($name)
43//! };
44//! }
45//!
46//! // Invoke `name_of_struct` with the definition of `MyStruct`.
47//! assert_eq!(my_struct!(name_of_struct!()), "MyStruct");
48//! ```
49//!
50//! ## `#![no_std]`
51//! [`mini_macro_magic`][self] is `#![no_std]`, it can be used anywhere Rust can.
52//!
53//! # Minimum Supported Rust Version
54//!
55//! Requires Rust 1.56.0.
56//!
57//! This crate follows the ["Latest stable Rust" policy](https://gist.github.com/alexheretic/d1e98d8433b602e57f5d0a9637927e0c). The listed MSRV won't be changed unless needed.
58//! However, updating the MSRV anywhere up to the latest stable at time of release
59//! is allowed.
60
61/// Identity macro that just outputs what it is given.
62///
63/// This can be used with a [`export!()`] generated macro
64/// to emit the exported tokens. See the [`example`] module for an example
65/// of it's usage.
66#[macro_export]
67macro_rules! emit {
68 {{$($t:tt)*}} => {$($t)*};
69}
70
71/// Export tokens for later inspection by other macros.
72///
73/// This macro enables exporting arbitrary token sequences to other
74/// modules or even crates. Call the macro with the following form.
75/// ```ignore
76/// export!(
77/// #[export(
78/// /// Any extra docs you want.
79/// <visibility> <macro name>$
80/// )]
81/// {
82/// <some arbitrary tokens>
83/// }
84/// );
85/// ```
86/// This will generate a `macro_rules!` with the name given at `<macro name>`.
87/// Any docs given in the `export` attribute will be added to the top of the macro
88/// docs. The macro is already given a set of basic docs and an example (see [`example::demo_struct`]
89/// for what this basic docs looks like).
90///
91/// `<visibility>` can be any of the normal visibility specifiers (nothing, `pub`, `pub(crate)`,
92/// `pub(super)`, ...). It can also be `pub crate` which has a special meaning. `pub crate` is used
93/// when in the root of a crate (`main.rs` or `lib.rs`) and we want the macro to be public outside
94/// the crate. This is needed because the normal visibility specifiers don't work there.
95///
96/// The public visibility specifiers also automatically put the macro's docs in the module
97/// [`export`] was invoked in instead of having the macro docs at the root of the crate.
98///
99/// The `$` after the macro name is always required. This is do to a limitation of `macro_ruiles!`
100/// where `$` can't be escaped.
101///
102/// The form above was chossen because it allows code formatting to work as you would expect.
103/// Any code in `<some arbitrary tokens>` is seen by rustfmt as a normal expression and can be
104/// formatted accordingly. rustfmt will not format the `export` attribute though.
105///
106/// *Note:* The docs generated for the macro include a doc test example. The doc test should always
107/// pass (if it doesn't please post an issue). However, sometimes this
108/// may not be wanted. To disable the doc test generation (it will be generated as a text code
109/// block instead) add the `mmm_no_gen_examples` cfg item (its not a feature) when compiling.
110///
111/// ### `macro_rules_attribute` Support
112/// There is also another form [`export`] can be called with designed for use with the
113/// [`macro_rules_attribute`](https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/) crate.
114/// ```ignore
115/// export!(
116/// /// Any docs or attributes for the item (not the macro).
117/// #[custom(export(
118/// /// Any extra docs you want.
119/// <visibility> <macro name>$
120/// ))]
121/// <some arbitrary tokens>
122/// );
123/// ```
124/// When used with the [`macro_rules_attribute::derive`](https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/attr.derive.html) macro this allows the following.
125/// ```
126/// # use mini_macro_magic::export;
127/// # use macro_rules_attribute::derive;
128/// /// Some docs for `Demo`.
129/// #[derive(export!)]
130/// #[custom(export(
131/// pub demo_struct$
132/// ))]
133/// struct Demo {
134/// pub name: String,
135/// }
136/// ```
137///
138/// # Examples
139///
140/// See the [integration tests](https://gitlab.com/konnorandrews/mini-macro-magic/-/tree/main/tests) for examples
141/// that show *why* you would want to use [`export`].
142///
143/// ```
144/// # use mini_macro_magic::export;
145/// export!(
146/// #[export(
147/// demo_struct$
148/// )]
149/// {
150/// struct Demo {
151/// pub name: String,
152/// }
153/// }
154/// );
155/// ```
156///
157/// ```
158/// # use mini_macro_magic::export;
159/// export!(
160/// #[export(
161/// pub a_plus_b$
162/// )]
163/// {
164/// a + b
165/// }
166/// );
167/// ```
168///
169/// ```
170/// # use mini_macro_magic::export;
171/// export!(
172/// #[export(
173/// /// Some docs.
174/// pub(crate) allow_unused$
175/// )]
176/// {
177/// #[allow(unused)]
178/// }
179/// );
180/// ```
181///
182/// # Invalid Input
183///
184/// If [`export`] is called with incorrect syntax a compile error will be generated with the
185/// expected calling syntax and the syntax it was given.
186///
187/// ```compile_fail
188/// # use mini_macro_magic::export;
189/// export!(
190/// #[export(demo_struct)]
191/// {
192/// struct Demo {
193/// pub name: String,
194/// }
195/// }
196/// );
197/// ```
198/// ```text
199/// error: `export!()` expects input of the form:
200///
201/// #[export(<vis> <name>$)] { <tokens> }
202///
203/// instead got:
204///
205/// #[export(demo_struct)] { struct Demo { pub name : String, } }
206///
207/// --> src/lib.rs:132:1
208/// |
209/// 6 | / export!(
210/// 7 | | #[export(demo_struct)]
211/// 8 | | {
212/// 9 | | struct Demo {
213/// ... |
214/// 12 | | }
215/// 13 | | );
216/// | |_^
217/// ```
218///
219#[macro_export]
220macro_rules! export {
221 {
222 #[export(
223 $(#[$($attr:tt)*])*
224 $name:ident $dollar:tt
225 )]
226 {
227 $($t:tt)*
228 }
229 } => {
230 $crate::__export_impl! {
231 $(#[$($attr)*])*
232 $name $dollar
233
234 {
235 use $name; // The standard use trick to make the macro an item.
236 }
237
238 {$($t)*}
239 }
240 };
241 {
242 #[export(
243 $(#[$($attr:tt)*])*
244 pub($($module:tt)*) $name:ident $dollar:tt
245 )]
246 {
247 $($t:tt)*
248 }
249 } => {
250 $crate::__export_impl! {
251 $(#[$($attr)*])*
252 $name $dollar
253
254 {
255 pub($($module)*) use $name; // The standard use trick to make the macro an item.
256 }
257
258 {$($t)*}
259 }
260 };
261 {
262 #[export(
263 $(#[$($attr:tt)*])*
264 pub $name:ident $dollar:tt
265 )]
266 {
267 $($t:tt)*
268 }
269 } => {
270 $crate::__export_impl! {
271 $(#[$($attr)*])*
272 #[doc(hidden)] // Hides the docs from the crate root.
273 #[macro_export]
274 $name $dollar
275
276 // This makes the macro appear as a normal item with it's docs.
277 {
278 #[doc(inline)]
279 pub use $name;
280 }
281
282 {$($t)*}
283 }
284 };
285 {
286 #[export(
287 $(#[$($attr:tt)*])*
288 pub crate $name:ident $dollar:tt
289 )]
290 {
291 $($t:tt)*
292 }
293 } => {
294 $crate::__export_impl! {
295 $(#[$($attr)*])*
296 #[macro_export]
297 $name $dollar
298
299 {} // A public macro at the crate root can't have a use.
300
301 {$($t)*}
302 }
303 };
304 {$($t:tt)*} => {
305 // Assume all other invocations are using the derive form.
306 //
307 // If the syntax is wrong, then find_custom_export_attr will
308 // emit a compile error.
309 $crate::__find_custom_export_attr! { {} {$($t)*} }
310 };
311}
312
313/// Unified impl for all the export macro variants.
314#[doc(hidden)]
315#[macro_export]
316macro_rules! __export_impl {
317 (
318 // Any attributes for the macro.
319 $(#[$($attr:tt)*])*
320
321 // The name of the macro.
322 $name:ident
323
324 // Needed to inject the $ token into the generated macro_rules.
325 $dollar:tt
326
327 // The `use` statements wanted.
328 {$($use:tt)*}
329
330 // The actual tokens to export.
331 {$($t:tt)*}
332 ) => {
333 $crate::__check_dollar!($dollar);
334
335 $(#[$($attr)*])*
336 ///
337 /// Injects the exported tokens (see section below) into the given macro call.
338 /// The exported tokens are injected in a `{}` block as the first thing.
339 /// For example with `some_macro!(a + b)` the actual macro call would look like
340 /// `some_macro!({ ... <exported tokens> ... } a + b)`.
341 ///
342 /// <details>
343 /// <summary>Expand to show exported tokens</summary>
344 ///
345 /// *Note: The tokens here are formatted via [`stringify!()`] so may not be very
346 /// readable.*
347 ///
348 /// ```text
349 #[doc = stringify!($($t)*)]
350 /// ```
351 ///
352 /// </details>
353 ///
354 /// # Examples
355 ///
356 #[cfg_attr(mmm_no_gen_examples, doc = "```text")]
357 #[cfg_attr(not(mmm_no_gen_examples), doc = "```")]
358 #[doc = concat!("use ", module_path!(), "::", stringify!($name), ";")]
359 ///
360 /// // Stringify all the exported tokens. The exported tokens are passed as a `{}`
361 /// // block to `stringify!()` at the beginning.
362 #[doc = concat!("let as_string = ", stringify!($name), "!(stringify!(some extra tokens));")]
363 ///
364 /// // Check that it matches what we expect.
365 #[doc = concat!("assert_eq!(as_string, r#####\"", stringify!({$($t)*} some extra tokens), "\"#####);")]
366 /// ```
367 macro_rules! $name {
368 ($dollar ($dollar t:tt)*) => {
369 $crate::__invoke! { {} {$dollar ($dollar t)*} {$($t)*} }
370 }
371 }
372
373 $($use)*
374 };
375}
376
377/// TT muncher to find the path for a macro invocation.
378///
379/// `some::path::macro!(some stuff)`
380/// this finds the `some::path::macro` and allows injecting extra tokens
381/// as a `{ ... tokens ... }` as the first token of the invocation.
382#[doc(hidden)]
383#[macro_export]
384macro_rules! __invoke {
385 // `{<path>} {!(<arg tokens>)} {<extra tokens>}`
386 //
387 // This arm invokes the macro.
388 ({$($path:tt)*} {!($($t:tt)*)} {$($extra:tt)*}) => {
389 $($path)*!({$($extra)*} $($t)*);
390 };
391 // `{<path>} {![<arg tokens>]} {<extra tokens>}`
392 //
393 // This arm invokes the macro.
394 ({$($path:tt)*} {![$($t:tt)*]} {$($extra:tt)*}) => {
395 $($path)*![{$($extra)*} $($t)*];
396 };
397 // `{<path>} {!{<arg tokens>}} {<extra tokens>}`
398 //
399 // This arm invokes the macro.
400 ({$($path:tt)*} {!{$($t:tt)*}} {$($extra:tt)*}) => {
401 $($path)*!{ {$($extra)*} $($t)* }
402 };
403 // `{<part of path>} {<next token> <arg tokens>}} {<extra tokens>}`
404 //
405 // This arm recurses until it parses out the path.
406 ({$($path:tt)*} {$next:tt $($t:tt)*} {$($extra:tt)*}) => {
407 $crate::__invoke! { {$($path)* $next} {$($t)*} {$($extra)*} }
408 };
409}
410
411/// Check that the passed in token is actually a literal `$`.
412///
413/// If it's not then a compile error is produced.
414#[doc(hidden)]
415#[macro_export]
416macro_rules! __check_dollar {
417 ($) => {};
418 ($other:tt) => {compile_error! { concat!("`export!()` expects `$` after macro name, got `", stringify!($other), "`") }};
419}
420
421/// TT muncher to find the `#[custom(export(...))]` attribute.
422#[doc(hidden)]
423#[macro_export]
424macro_rules! __find_custom_export_attr {
425 ({$($attr:tt)*} {#[custom(export($($stuff:tt)*))] $($t:tt)*}) => {
426 $crate::export! {
427 #[export($($stuff)*)]
428 {
429 $($attr)*
430 $($t)*
431 }
432 }
433 };
434 ({$($attr:tt)*} {$next:tt $($t:tt)*}) => {
435 $crate::__find_custom_export_attr! {
436 {$($attr)* $next}
437 {$($t)*}
438 }
439 };
440 ({$($a:tt)*} {$($t:tt)*}) => {
441 compile_error! { concat!(
442"`export!()` expects input of the form:
443
444#[export(<vis> <name>$)] { <tokens> }
445
446instead got:
447
448",
449stringify!($($a)* $($t)*),
450"
451\n")
452 }
453 }
454}
455
456/// Example of using [`export`] and [`emit`].
457///
458/// This module is not part of the crate's public API and is only visible on docs.
459///
460/// Code used for this module:
461/// ```
462/// pub mod example {
463/// mini_macro_magic::export!(
464/// #[export(
465/// /// This is the macro generated by the example.
466/// pub demo_struct$
467/// )]
468/// {
469/// /// Demo struct definition.
470/// pub struct Demo {
471/// /// The X value.
472/// pub x: i32,
473///
474/// /// The Y value.
475/// pub y: i32,
476/// }
477/// }
478/// );
479///
480/// // Emit the struct definition for Demo.
481/// demo_struct!(mini_macro_magic::emit!());
482/// }
483/// ```
484///
485/// [`Demo`](example::Demo) is a struct definition exported by [`export`].
486/// As you can see it exists as expected as a normal struct because we emitted
487/// the tokens into the module with [`emit`].
488///
489/// [`demo_struct`] is the macro that was
490/// generated by [`export`].
491/// This is the macro you would call to inspect the tokens of [`Demo`](example::Demo).
492#[cfg(doc_example)]
493pub mod example {
494 export!(
495 #[export(
496 /// This is the macro generated by the example.
497 pub demo_struct$
498 )]
499 {
500 /// Demo struct definition.
501 pub struct Demo {
502 /// The X value.
503 pub x: i32,
504
505 /// The Y value.
506 pub y: i32,
507 }
508 }
509 );
510
511 // Emit the struct definition for Demo.
512 demo_struct!(emit!());
513
514 #[test]
515 fn check_demo_exists() {
516 let _x = Demo { x: 1, y: 2 };
517 }
518}
519
520#[doc(hidden)]
521#[cfg(not(doc_example))]
522pub mod example {
523 #[doc(hidden)]
524 pub mod demo_struct {}
525}