eval_macro/lib.rs
1//! <img width="680" alt="banner" src="https://github.com/user-attachments/assets/e7dab624-6e88-41f6-b681-a7a430f96b50">
2//!
3//! <br/>
4//! <br/>
5//!
6//! # π Eval Macro
7//!
8//! **Eval Macro** introduces a new macro type for Rust, blending power and ease of use. Hereβs how
9//! it compares to `macro_rules!` and procedural macros:
10//!
11//! | | Proc Macro | Eval Macro | Macro Rules |
12//! | :--- | :--- | :--- | :--- |
13//! | **Input** | [Token Stream][1] | **Rust Code** or [Token Stream][1] | [Macro Fragments][2] |
14//! | **Output** | [Token Stream][1] | **Rust Code** or [Token Stream][1] | [Macro Fragments][2] |
15//! | **Advanced transformations** | β
| β
| β |
16//! | **Easy to define** | β | β
| β
|
17//! | **Easy to read** | β | β
| β
|
18//! | **Reusable** | β
| β | β
|
19//! | **Hygienic** | β | β | β
|
20//!
21//! [1]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html
22//! [2]: https://doc.rust-lang.org/reference/macros-by-example.html#metavariables
23//!
24//! In short, Eval Macros provide even greater flexibility and power than procedural macros, while
25//! preserving the simplicity of `macro_rules!` macros. However, they are not reusable β you cannot
26//! export an Eval Macro for use in other crates.
27//!
28//! <br/>
29//! <br/>
30//!
31//! # π€© Syntax
32//!
33//! Use the `eval!` macro to create and run an Eval Macro inline. The content of the macro is
34//! regular Rust code, which will be compiled and executed at build time. Inside the `eval!`
35//! block, you can use the `output!` macro to emit Rust code. `output!` supports double-brace
36//! interpolation, allowing you to embed variables directly into the generated code.
37//!
38//! Example:
39//!
40//! ```
41//! use eval_macro::eval;
42//!
43//! eval! {
44//! let components = ["X", "Y", "Z", "W"];
45//! for (ix, name) in components.iter().enumerate() {
46//!
47//! // === Structs Definitions ===
48//! let dim = ix + 1;
49//! let cons = components[0..dim].join(",");
50//! output! {
51//! enum Position{{dim}} {
52//! {{cons}}
53//! }
54//! }
55//!
56//! // === Conversions ===
57//! for ix2 in (dim + 1)..=components.len() {
58//! let source = format!("Position{dim}");
59//! let branches = components[0..dim].iter().map(|comp|
60//! format!("{source}::{comp} => Self::{comp}")
61//! ).collect::<Vec<_>>().join(",");
62//! output! {
63//! impl From<{{source}}> for Position{{ix2}} {
64//! fn from(src: {{source}}) -> Self {
65//! match src {
66//! {{branches}}
67//! }
68//! }
69//! }
70//! }
71//! }
72//! }
73//! }
74//! # fn main() {}
75//! ```
76//!
77//! This will generate:
78//!
79//! ```
80//! enum Position1 { X }
81//! enum Position2 { X, Y }
82//! enum Position3 { X, Y, Z }
83//! enum Position4 { X, Y, Z, W }
84//!
85//! impl From<Position1> for Position2 {
86//! fn from(src: Position1) -> Self {
87//! match src {
88//! Position1::X => Self::X
89//! }
90//! }
91//! }
92//! impl From<Position1> for Position3 {
93//! fn from(src: Position1) -> Self {
94//! match src {
95//! Position1::X => Self::X
96//! }
97//! }
98//! }
99//! impl From<Position1> for Position4 {
100//! fn from(src: Position1) -> Self {
101//! match src {
102//! Position1::X => Self::X
103//! }
104//! }
105//! }
106//!
107//! impl From<Position2> for Position3 {
108//! fn from(src: Position2) -> Self {
109//! match src {
110//! Position2::X => Self::X,
111//! Position2::Y => Self::Y
112//! }
113//! }
114//! }
115//! impl From<Position2> for Position4 {
116//! fn from(src: Position2) -> Self {
117//! match src {
118//! Position2::X => Self::X,
119//! Position2::Y => Self::Y
120//! }
121//! }
122//! }
123//!
124//! impl From<Position3> for Position4 {
125//! fn from(src: Position3) -> Self {
126//! match src {
127//! Position3::X => Self::X,
128//! Position3::Y => Self::Y,
129//! Position3::Z => Self::Z
130//! }
131//! }
132//! }
133//! # fn main() {}
134//! ```
135//!
136//! Doing this with `macro_rules!` or procedural macros would be far more complex!
137//!
138//! Also, please note that you can use the `eval!` macro to perform complex type-level
139//! calculations:
140//!
141//! ```
142//! use eval_macro::eval;
143//!
144//! const MY_NUM: usize = eval! { (std::f32::consts::PI.sqrt() * 10.0).round() as usize };
145//! # fn main() {}
146//! ```
147//!
148//! <br/>
149//! <br/>
150//!
151//! # πͺ² Logging and Output Protocol
152//!
153//! During compilation, `eval!` blocks can print messages directly to `stdout` and `stderr`.
154//! This allows you to emit debug information, diagnostics, and the generated code itself during
155//! macro evaluation. Communication between the `eval!` macro and the build system follows a
156//! simple line-based protocol. Each printed line can optionally begin with a prefix,
157//! indicating its purpose:
158//!
159//! | Prefix | Meaning |
160//! | :--- | :--- |
161//! | _(none)_ | Debug log message (informational output). |
162//! | `OUTPUT:` | A line of generated Rust code to be included in the final macro output. |
163//! | `WARNING:` | A compilation warning. This is printed to `stdout` until [Procedural Macro Diagnostics][3] is stabilized. |
164//! | `ERROR:` | A compilation error. This is printed to `stdout` until [Procedural Macro Diagnostics][3] is stabilized. |
165//!
166//! ### Utility Functions and Macros
167//!
168//! To simplify working with this protocol, `eval!` blocks have access to a set of helper
169//! functions and macros that automatically apply the correct prefixes to each line.
170//!
171//! #### Functions
172//!
173//! These functions allow you to transform multi-line strings by adding the appropriate prefixes:
174//!
175//! ```rust
176//! fn prefix_lines_with(prefix: &str, input: &str) -> String {
177//! // Adds the given prefix to each line of the input string.
178//! # panic!()
179//! }
180//!
181//! fn prefix_lines_with_output(input: &str) -> String {
182//! // Adds `OUTPUT:` to each line of the input string.
183//! # panic!()
184//! }
185//!
186//! fn prefix_lines_with_warning(input: &str) -> String {
187//! // Adds `WARNING:` to each line of the input string.
188//! # panic!()
189//! }
190//!
191//! fn prefix_lines_with_error(input: &str) -> String {
192//! // Adds `ERROR:` to each line of the input string.
193//! # panic!()
194//! }
195//! ```
196//!
197//! #### Macros
198//!
199//! These macros allow you to directly print prefixed lines to `stdout`, following the
200//! protocol:
201//!
202//! ```rust
203//! macro_rules! println_output {
204//! // Prints a line prefixed with `OUTPUT:`.
205//! # () => {};
206//! }
207//!
208//! macro_rules! println_warning {
209//! // Prints a line prefixed with `WARNING:`.
210//! # () => {};
211//! }
212//!
213//! macro_rules! println_error {
214//! // Prints a line prefixed with `ERROR:`.
215//! # () => {};
216//! }
217//! ```
218//!
219//! These tools ensure consistent formatting and correct communication between `eval!` blocks
220//! and the build system, reducing the risk of malformed output.
221//!
222//! [3]: https://github.com/rust-lang/rust/issues/54140
223//!
224//! <br/>
225//! <br/>
226//!
227//! # π Attributes
228//!
229//! The `eval!` macro supports global attributes that can be placed at the top of the block.
230//! These attributes allow you to customize both the project's Cargo configuration and its
231//! project-wide attributes.
232//!
233//! ### Supported Cargo Configuration Attributes
234//!
235//! | Attribute | Default |
236//! | :--- | :--- |
237//! | `#![edition(...)]` | `2024` |
238//! | `#![resolver(...)]` | `3` |
239//! | `#![dependency(...)]`| `[]` |
240//!
241//! ### Supported Standard Attributes
242//!
243//! In addition to Cargo settings, the following standard Rust attributes are supported:
244//!
245//! - `#![feature(...)]`
246//! - `#![allow(...)]`
247//! - `#![expect(...)]`
248//! - `#![warn(...)]`
249//! - `#![deny(...)]`
250//! - `#![forbid(...)]`
251//!
252//! Example:
253//!
254//! ```rust
255//! use eval_macro::eval;
256//!
257//! eval! {
258//! #![edition(2024)]
259//! #![resolver(3)]
260//! #![dependency(anyhow = "1.0")]
261//!
262//! type Result<T> = anyhow::Result<T>;
263//! // ...
264//! }
265//! # fn main() {}
266//! ```
267//!
268//! This system allows each `eval!` macro block to define its own dependencies and configuration
269//! without affecting your project's main `Cargo.toml` or global settings.
270//!
271//! <br/>
272//! <br/>
273//!
274//! # π§± Working with Token Streams
275//!
276//! If you prefer to work directly with token streams instead of plain Rust code, you can
277//! leverage the `proc-macro2` crate to parse source code into a `TokenStream` and then
278//! generate output using the `quote` crate.
279//!
280//! This allows you to process and manipulate Rust code programmatically within an `eval!` block,
281//! similar to how procedural macros operate β but with the flexibility of the `eval!` environment.
282//!
283//! ```
284//! use eval_macro::eval;
285//!
286//! eval! {
287//! #![dependency(proc-macro2 = "1")]
288//! #![dependency(quote = "1")]
289//! use proc_macro2::TokenStream;
290//! use quote::quote;
291//! let tokens: TokenStream = SOURCE_CODE.parse().unwrap();
292//! // ...
293//! let out = quote! {
294//! pub struct Test {}
295//! };
296//! println_output!("{}", out.to_string());
297//! }
298//!
299//! type Alias = Test;
300//!
301//! # fn main() {}
302//! ```
303//!
304//! <br/>
305//! <br/>
306//!
307//! # π How It Works Under The Hood
308//!
309//! The content inside `eval!` is pasted into the `main` function of a temporary Rust project
310//! created in `$HOME/.cargo/eval-macro/<project-id>`. This project is created, compiled,
311//! executed, and removed at build time, and its `stdout` becomes the generated Rust code. The
312//! generated `main` function looks something like this:
313//!
314//! ```
315//! const SOURCE_CODE: &str = "..."; // Your code as a string.
316//!
317//! fn main() {
318//! let mut output_buffer = String::new();
319//! let result = {{
320//! // Your code.
321//! }};
322//! push_as_str(&mut output_buffer, &result);
323//! println!("{}", prefix_lines_with_output(&output_buffer));
324//! }
325//! # fn push_as_str(str: &mut String, result: &()) {}
326//! # fn prefix_lines_with_output(input: &str) -> String { String::new() }
327//! ```
328//!
329//! The `output!` macro is essentially a shortcut for writing to `output_buffer` using `format!`,
330//! so this:
331//!
332//! ```
333//! use eval_macro::eval;
334//!
335//! eval! {
336//! let components = ["X", "Y", "Z", "W"];
337//! for (ix, name) in components.iter().enumerate() {
338//! let dim = ix + 1;
339//! let cons = components[0..dim].join(",");
340//! output! {
341//! enum Position{{dim}} {
342//! {{cons}}
343//! }
344//! }
345//! }
346//! }
347//! # fn main() {}
348//! ```
349//!
350//! Is equivalent to:
351//!
352//! ```
353//! use eval_macro::eval;
354//!
355//! eval! {
356//! let components = ["X", "Y", "Z", "W"];
357//! for (ix, name) in components.iter().enumerate() {
358//! let dim = ix + 1;
359//! let cons = components[0..dim].join(",");
360//! // The `write_ln!` macro is delivered by this library.
361//! write_ln!(output_buffer, "
362//! enum Position{dim} {{
363//! {cons}
364//! }}
365//! ");
366//! }
367//! }
368//! # fn main() {}
369//! ```
370//!
371//! And that, in turn, is just shorthand for:
372//!
373//! ```
374//! use eval_macro::eval;
375//!
376//! eval! {
377//! let components = ["X", "Y", "Z", "W"];
378//! for (ix, name) in components.iter().enumerate() {
379//! let dim = ix + 1;
380//! let cons = components[0..dim].join(",");
381//! output_buffer.push_str(
382//! &format!("
383//! enum Position{dim} {{
384//! {cons}
385//! }}
386//! ")
387//! );
388//! }
389//! }
390//! # fn main() {}
391//! ```
392//!
393//! Which, ultimately, is equivalent to:
394//!
395//! ```
396//! use eval_macro::eval;
397//!
398//! eval! {
399//! let components = ["X", "Y", "Z", "W"];
400//! for (ix, name) in components.iter().enumerate() {
401//! let dim = ix + 1;
402//! let cons = components[0..dim].join(",");
403//! println!("OUTPUT: enum Position{dim} {{");
404//! println!("OUTPUT: {cons}");
405//! println!("OUTPUT: }}");
406//! }
407//! }
408//! # fn main() {}
409//! ```
410//!
411//! <br/>
412//! <br/>
413//!
414//! # β οΈ Troubleshooting
415//!
416//! β οΈ **Note:** Rust IDEs differ in how they handle macro expansion. This macro is tuned for
417//! `RustRoverβs` expansion engine.
418//!
419//! If your IDE struggles to correctly expand `eval!`, you can manually switch to the `write_ln!`
420//! syntax described above. If you encounter issues, please
421//! [open an issue](https://github.com/wdanilo/eval-macro/issues) to let us know!
422
423pub use eval_macro_internal::*;