eval_macro/lib.rs
1//! <img width="680" alt="banner" src="https://github.com/user-attachments/assets/bd61b780-e812-47eb-ad1d-e2bebb675805">
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 | Rust Code | Macro Fragments |
14//! | **Output** | Token Stream | Rust Code | Macro Fragments |
15//! | **Hygienic** | β | β | β
|
16//! | **Advanced transformations** | β
| β
| β |
17//! | **Easy to define** | β | β
| β
|
18//! | **Easy to read** | β | β
| β
|
19//! | **Reusable** | β
| β | β
|
20//!
21//! In short, **Eval Macros** offer procedural macro power with `macro_rules!` simplicity. However,
22//! they are **not reusable** β you cannot export an Eval Macro for use in other crates.
23//!
24//! <br/>
25//! <br/>
26//!
27//! # π€© Syntax
28//!
29//! Use the `eval!` macro to create and run an Eval Macro inline. The content of the macro is
30//! **regular Rust code**, which will be compiled and executed at build time.
31//!
32//! Inside the `eval!` block, you can use the `output!` macro to emit Rust code. `output!` supports
33//! **double-brace interpolation**, allowing you to embed variables directly into the generated
34//! code.
35//!
36//! Example:
37//!
38//! ```
39//! use eval_macro::eval;
40//!
41//! eval! {
42//! let components = ["X", "Y", "Z", "W"];
43//! for (ix, name) in components.iter().enumerate() {
44//!
45//! // === Structs Definitions ===
46//! let dim = ix + 1;
47//! let cons = components[0..dim].join(",");
48//! output! {
49//! enum Position{{dim}} {
50//! {{cons}}
51//! }
52//! }
53//!
54//! // === Conversions ===
55//! for ix2 in (dim + 1)..=components.len() {
56//! let source = format!("Position{dim}");
57//! let branches = components[0..dim].iter().map(|comp|
58//! format!("{source}::{comp} => Self::{comp}")
59//! ).collect::<Vec<_>>().join(",");
60//! output! {
61//! impl From<{{source}}> for Position{{ix2}} {
62//! fn from(src: {{source}}) -> Self {
63//! match src {
64//! {{branches}}
65//! }
66//! }
67//! }
68//! }
69//! }
70//! }
71//! }
72//! # fn main() {}
73//! ```
74//!
75//! This will generate:
76//!
77//! ```
78//! enum Position1 { X }
79//! enum Position2 { X, Y }
80//! enum Position3 { X, Y, Z }
81//! enum Position4 { X, Y, Z, W }
82//!
83//! impl From<Position1> for Position2 {
84//! fn from(src: Position1) -> Self {
85//! match src {
86//! Position1::X => Self::X
87//! }
88//! }
89//! }
90//! impl From<Position1> for Position3 {
91//! fn from(src: Position1) -> Self {
92//! match src {
93//! Position1::X => Self::X
94//! }
95//! }
96//! }
97//! impl From<Position1> for Position4 {
98//! fn from(src: Position1) -> Self {
99//! match src {
100//! Position1::X => Self::X
101//! }
102//! }
103//! }
104//!
105//! impl From<Position2> for Position3 {
106//! fn from(src: Position2) -> Self {
107//! match src {
108//! Position2::X => Self::X,
109//! Position2::Y => Self::Y
110//! }
111//! }
112//! }
113//! impl From<Position2> for Position4 {
114//! fn from(src: Position2) -> Self {
115//! match src {
116//! Position2::X => Self::X,
117//! Position2::Y => Self::Y
118//! }
119//! }
120//! }
121//!
122//! impl From<Position3> for Position4 {
123//! fn from(src: Position3) -> Self {
124//! match src {
125//! Position3::X => Self::X,
126//! Position3::Y => Self::Y,
127//! Position3::Z => Self::Z
128//! }
129//! }
130//! }
131//! # fn main() {}
132//! ```
133//!
134//! Doing this with `macro_rules!` or procedural macros would be far more complex!
135//!
136//! <br/>
137//! <br/>
138//!
139//! # π How It Works
140//!
141//! The content inside `eval!` is **pasted into the `main` function** of a temporary Rust project.
142//! This project is **compiled and executed at build time**, and its `stdout` becomes the generated
143//! Rust code. The generated `main` function looks something like this:
144//!
145//! ```ignore
146//! fn main() {
147//! let mut output_buffer = String::new();
148//! {your_code}
149//! println!("{{output_buffer}}");
150//! }
151//! ```
152//!
153//! The `output!` macro is essentially a shortcut for writing to `output_buffer` using `format!`,
154//! so this:
155//!
156//! ```
157//! use eval_macro::eval;
158//!
159//! eval! {
160//! let components = ["X", "Y", "Z", "W"];
161//! for (ix, name) in components.iter().enumerate() {
162//! let dim = ix + 1;
163//! let cons = components[0..dim].join(",");
164//! output! {
165//! enum Position{{dim}} {
166//! {{cons}}
167//! }
168//! }
169//! }
170//! }
171//! # fn main() {}
172//! ```
173//!
174//! Is equivalent to:
175//!
176//! ```
177//! use eval_macro::eval;
178//!
179//! eval! {
180//! let components = ["X", "Y", "Z", "W"];
181//! for (ix, name) in components.iter().enumerate() {
182//! let dim = ix + 1;
183//! let cons = components[0..dim].join(",");
184//! write_ln!(output_buffer, "
185//! enum Position{dim} {{
186//! {cons}
187//! }}
188//! ");
189//! }
190//! }
191//! # fn main() {}
192//! ```
193//!
194//! And that, in turn, is just shorthand for:
195//!
196//! ```
197//! use eval_macro::eval;
198//!
199//! eval! {
200//! let components = ["X", "Y", "Z", "W"];
201//! for (ix, name) in components.iter().enumerate() {
202//! let dim = ix + 1;
203//! let cons = components[0..dim].join(",");
204//! output_buffer.push_str(&format!("
205//! enum Position{dim} {{
206//! {cons}
207//! }}
208//! "));
209//! }
210//! }
211//! # fn main() {}
212//! ```
213//!
214//! <br/>
215//! <br/>
216//!
217//! # π Dependencies
218//!
219//! Each `eval!` block can define **its own Cargo dependencies**, allowing you to pull in external
220//! crates directly within the macro context. This is done using a special **pragma attribute**:
221//! `#![dependency(...)]`.
222//!
223//! ```
224//! use eval_macro::eval;
225//!
226//! eval! {
227//! #![dependency(anyhow = "1.0")]
228//! type Result<T> = anyhow::Result<T>;
229//! // ...
230//! }
231//! # fn main() {}
232//! ```
233//! This flexibility allows `eval!` macros to seamlessly leverage third-party crates, without
234//! affecting your project's main `Cargo.toml`.
235//!
236//! <br/>
237//! <br/>
238//!
239//! # β οΈ Troubleshooting
240//!
241//! β οΈ **Note:** Rust IDEs differ in how they handle macro expansion. This macro is tuned for
242//! `RustRoverβs` expansion engine.
243//!
244//! If your IDE struggles to correctly expand `eval!`, you can manually switch to the `write_ln!`
245//! syntax described above. If you encounter issues, please
246//! [open an issue](https://github.com/wdanilo/eval-macro/issues) to let us know!
247
248pub use eval_macro_internal::*;