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::*;