crabtime/lib.rs
1//! # π¦ Crabtime
2//!
3//! <img width="680" alt="banner" src="https://github.com/user-attachments/assets/d273e38d-951c-4183-b42e-ac5bdf939d69">
4//!
5//! <br/>
6//! <br/>
7//!
8//! **Crabtime** offers a novel way to write Rust macros, inspired by
9//! [Zig's comptime][zigs_comptime]. It provides even more flexibility and power than procedural
10//! macros, while remaining easier and more natural to read and write than
11//! [`macro_rules!`][macro_rules].
12//!
13//! <br/>
14//! <br/>
15//!
16//! # π Comparison to Proc Macros and `macro_rules!`
17//!
18//! Below is a comparison of key aspects of Rust's macro systems:
19//!
20//! <h5><b>Input/Output</b></h5>
21//!
22//! | <div style="width:300px"/> | Crabtime | Proc Macro | `macro_rules!` |
23//! | :--- | :--- | :--- | :--- |
24//! | Input as [Token Stream][token_stream] | β
| β
| β |
25//! | Input as [Macro Fragments][macro_fragments] | β
| β | β
|
26//! | Input as Rust Code (String) | β
| β | β |
27//! | Output as [Token Stream][token_stream] | β
| β
| β |
28//! | Output as [Macro Fragments Template][macro_fragments] | β
| β | β
|
29//! | Output as Rust Code (String) | β
| β | β |
30//!
31//! <h5><b>Functionalities</b></h5>
32//!
33//! | <div style="width:300px"/> | Crabtime | Proc Macro | `macro_rules!` |
34//! | :--- | :--- | :--- | :--- |
35//! | Advanced transformations | β
| β
| β |
36//! | [Space-aware interpolation](#-output) | β
| β | β |
37//! | Can define [fn-like macros][fn_like_macros] | β
| β
| β
|
38//! | Can define [derive macros][derive_macros] | π§ | β
| β |
39//! | Can define [attribute macros][attribute_macros] | π§ | β
| β |
40//! | Reusable across modules and crates | β
| β
| β
|
41//!
42//! <h5><b>Comfort of life</b></h5>
43//!
44//! | <div style="width:300px"/> | Crabtime | Proc Macro | `macro_rules!` |
45//! | :--- | :--- | :--- | :--- |
46//! | Full expansion in IDEs[^supported_ides] | β
| β
| β
|
47//! | Full type hints in IDEs[^supported_ides] | β
| β
| β |
48//! | Works with [rustfmt][rustfmt] | β
| β
| β |
49//! | Easy to define (inline, the same crate) | β
| β | β
|
50//! | Easy to read | β
| β | β οΈ |
51//! | [Hygienic][macro_hygiene] | β | β | β
|
52//!
53//! <br/>
54//! <br/>
55//!
56//! # π― One-shot evaluation
57//!
58//! The simplest and least exciting use of Crabtime is straightforward compile-time code
59//! evaluation. To evaluate an expression and paste its output as new code, just use
60//! `crabtime::eval`, as shown below:
61//!
62//! ```
63//! const MY_NUM: usize = crabtime::eval! {
64//! (std::f32::consts::PI.sqrt() * 10.0).round() as usize
65//! };
66//! # fn main() {}
67//! ```
68//!
69//! <br/>
70//! <br/>
71//!
72//! # π€© Function-like macros
73//!
74//! Use the `crabtime::function` attribute to define a new [function-like macro][fn_like_macros].
75//! Crabtime will remove the annotated function and replace it with a macro definition of the same
76//! name. You can then call the macro to compile and execute the function at build time, and use
77//! its output as the generated Rust code. You can also use the standard `#[macro_export]`
78//! attribute to export your macro. Let's start with a simple example, and let's refine it down
79//! the line. Let's generate the following Rust code:
80//!
81//! ```
82//! enum Position1 { X }
83//! enum Position2 { X, Y }
84//! enum Position3 { X, Y, Z }
85//! enum Position4 { X, Y, Z, W }
86//! ```
87//!
88//! We can do it in this, not very exciting way:
89//!
90//! ```
91//! // Replaces the function definition with a `gen_positions1!` macro.
92//! #[crabtime::function]
93//! #[macro_export] // <- This is how you export it!
94//! fn gen_positions1() -> &str {
95//! "
96//! enum Position1 { X }
97//! enum Position2 { X, Y }
98//! enum Position3 { X, Y, Z }
99//! enum Position4 { X, Y, Z, W }
100//! "
101//! }
102//!
103//! // Compiles and evaluates the gen_positions1 function at build-time and
104//! // uses its output as the new code source.
105//! gen_positions1!();
106//! # fn main() {}
107//! ```
108//!
109//! <br/>
110//!
111//! <div class="warning">
112//! Due to limitations of `macro_rules!` (which `Crabtime` uses under the hood), you must either
113//! wrap the macro call in extra braces when using it in an expression context, or use
114//! `crabtime::expression` instead of `crabtime::function`, which does this step for you. For
115//! example, if you want to assign the macroβs output to a variable, do this:
116//!
117//! ```
118//! #[crabtime::expression]
119//! fn gen_expr() {
120//! let output_num = 3;
121//! crabtime::output! {
122//! {{output_num}}
123//! }
124//! }
125//!
126//! fn test() {
127//! let x = gen_expr!();
128//! }
129//! ```
130//!
131//! Alternatively, you can wrap the macro call in extra braces by yourself if you want to use the
132//! macro both in expression and statement contexts:
133//!
134//! ```
135//! #[crabtime::function]
136//! fn gen_expr() {
137//! let output_num = 3;
138//! crabtime::output! {
139//! {{output_num}}
140//! }
141//! }
142//!
143//! fn test() {
144//! // `let x = gen_expr!{};` would not work here!
145//! let x = { gen_expr!{} };
146//! }
147//! ```
148//!
149//! </div>
150//!
151//! <br/>
152//! <br/>
153//!
154//! # π€© Attribute and derive macros
155//! Currently, generating [attribute macros][attribute_macros] and [derive macros][derive_macros]
156//! is not supported, but there are several ways to achieve it. If you want to help, ping us on
157//! [GitHub](https://github.com/wdanilo/crabtime).
158//!
159//! <br/>
160//! <br/>
161//!
162//! # π€ Output
163//!
164//! There are several ways to generate the output code from a Crabtime macro. We recommend you to
165//! use either `crabtime::output!` or `crabtime::quote!` macros, as they allow for the most
166//! concise, easy-to-understand, and maintainable implementations. Supported input types are
167//! described later, for now just ignore them.
168//!
169//! <br/>
170//!
171//! <h5><b>Generating output by using <code>crabtime::output!</code></b></h5>
172//!
173//! The simplest and most recommended way to generate macro output is by using the
174//! `crabtime::output!` macro. It allows for space-aware variable interpolation. It's like the
175//! `format!` macro, but with inversed rules regarding curly braces β it preserves single braces
176//! and uses double braces for interpolation. Please note that it preserves spaces, so
177//! `Position {{ix}}` and `Position{{ix}}` mean different things, and the latter will generate
178//! `Position1`, `Position2`, etc.
179//!
180//! ```
181//! #[crabtime::function]
182//! fn gen_positions2(components: Vec<String>) {
183//! for dim in 1 ..= components.len() {
184//! let cons = components[0..dim].join(",");
185//! crabtime::output! {
186//! enum Position{{dim}} {
187//! {{cons}}
188//! }
189//! }
190//! }
191//! }
192//! gen_positions2!(["X", "Y", "Z", "W"]);
193//! # fn main() {}
194//! ```
195//!
196//! <br/>
197//!
198//! <div class="warning">
199//!
200//! β οΈ **Note:** Interpolated variables are inserted *as-is*, without additional quotes or escape
201//! characters.
202//!
203//! This means that if you want to insert a string literal, you must manually wrap or escape it.
204//! For example, the following code computes a `full_url` that includes quotes, because it is being
205//! inserted as a string literal into the `println!` macro within the generated code:
206//!
207//! ```rust
208//! #[crabtime::function]
209//! fn gen_urls(components: Vec<String>) {
210//! for (idx, path) in components.iter().enumerate() {
211//! let full_url = format!("\"{}\"", ["http://localhost:3000", path].join("/"));
212//! crabtime::output! {
213//! println!("{}", {{full_url}});
214//! }
215//! }
216//! }
217//!
218//! fn main() {
219//! gen_urls!(["home", "about", "contact"]);
220//! }
221//! ```
222//!
223//! </div>
224//!
225//! <br/>
226//!
227//! <h5><b>Generating output by using <code>crabtime::quote!</code></b></h5>
228//!
229//! The `crabtime::quote!` macro is just like `crabtime::output!`, but instead of outputting the
230//! code immediately, it returns it (as a `String`), so you can store it in a variable and re-use
231//! it across different subsequent calls to `crabtime::quote!` or `crabtime::output!`.
232//!
233//! ```
234//! #[crabtime::function]
235//! fn gen_positions3(components: Vec<String>) -> String {
236//! let structs = (1 ..= components.len()).map(|dim| {
237//! let cons = components[0..dim].join(",");
238//! crabtime::quote! {
239//! enum Position{{dim}} {
240//! {{cons}}
241//! }
242//! }
243//! }).collect::<Vec<String>>();
244//! structs.join("\n")
245//! }
246//! gen_positions3!(["X", "Y", "Z", "W"]);
247//! # fn main() {}
248//! ```
249//!
250//! <br/>
251//!
252//! <h5><b>Generating output by returning a string or number</b></h5>
253//!
254//! You can simply return a string or number from the function. It will be used as the generated
255//! macro code.
256//!
257//! ```
258//! #[crabtime::function]
259//! fn gen_positions4(components: Vec<String>) -> String {
260//! (1 ..= components.len()).map(|dim| {
261//! let cons = components[0..dim].join(",");
262//! format!("enum Position{dim} {{ {cons} }}")
263//! }).collect::<Vec<_>>().join("\n")
264//! }
265//! gen_positions4!(["X", "Y", "Z", "W"]);
266//! # fn main() {}
267//! ```
268//!
269//! <br/>
270//!
271//! <h5><b>Generating output by using <code>crabtime::output_str!</code></b></h5>
272//!
273//! Alternatively, you can use the `crabtime::output_str!` macro to immediately write strings to
274//! the code output buffer:
275//!
276//! ```
277//! #[crabtime::function]
278//! fn gen_positions5(components: Vec<String>) {
279//! for dim in 1 ..= components.len() {
280//! let cons = components[0..dim].join(",");
281//! crabtime::output_str!("enum Position{dim} {{ {cons} }}")
282//! }
283//! }
284//! gen_positions5!(["X", "Y", "Z", "W"]);
285//! # fn main() {}
286//! ```
287//!
288//! <br/>
289//!
290//! <h5><b>Generating output by returning a <code>TokenStream</code></b></h5>
291//!
292//! Finally, you can output [TokenStream][token_stream] from the macro. Please note that for
293//! brevity the below example uses [inline dependency injection](inline_dependency_injection),
294//! which is described later. In real code you should use your `Cargo.toml`'s
295//! `[build-dependencies]` section to include the necessary dependencies instead.
296//!
297//! ```
298//! #[crabtime::function]
299//! fn gen_positions6() -> proc_macro2::TokenStream {
300//! // Inline dependencies used for brevity.
301//! // You should use [build-dependencies] section in your Cargo.toml instead.
302//! #![dependency(proc-macro2 = "1")]
303//! #![dependency(syn = "2")]
304//! #![dependency(quote = "1")]
305//! use proc_macro2::Span;
306//! use quote::quote;
307//!
308//! let components = ["X", "Y", "Z", "W"];
309//! let defs = (1 ..= components.len()).map(|dim| {
310//! let cons = components[0..dim].iter().map(|t|
311//! syn::Ident::new(t, Span::call_site())
312//! );
313//! let ident = syn::Ident::new(&format!("Position{dim}"), Span::call_site());
314//! quote! {
315//! enum #ident {
316//! #(#cons),*
317//! }
318//! }
319//! }).collect::<Vec<_>>();
320//! quote! {
321//! #(#defs)*
322//! }
323//! }
324//! gen_positions6!();
325//! # fn main() {}
326//! ```
327//!
328//! <br/>
329//! <br/>
330//!
331//! # π₯ Input
332//!
333//! Similarly to generating output, there are several ways to parametrize macros and provide them
334//! with input at their call site. We recommend you use the pattern parametrization, as it's the
335//! simplest and easiest to maintain.
336//!
337//! <br/>
338//!
339//! <h5><b>Input by using supported arguments</b></h5>
340//!
341//! Currently, you can use any combination of the following types as arguments to your macro and
342//! they will be automatically translated to patterns: `Vec<...>`, `&str`, `String`, and numbers.
343//! If the expected argument is a string, you can pass either a string literal or an identifier,
344//! which will automatically be converted to a string.
345//!
346//! ```
347//! #[crabtime::function]
348//! fn gen_positions7(name: String, components: Vec<String>) {
349//! for dim in 1 ..= components.len() {
350//! let cons = components[0..dim].join(",");
351//! crabtime::output! {
352//! enum {{name}}{{dim}} {
353//! {{cons}}
354//! }
355//! }
356//! }
357//! }
358//! gen_positions7!(Position, ["X", "Y", "Z", "W"]);
359//! gen_positions7!(Color, ["R", "G", "B"]);
360//! # fn main() {}
361//! ```
362//!
363//! <br/>
364//!
365//! <h5><b>Input by using patterns</b></h5>
366//!
367//! In case you want even more control, you can use the same patterns as
368//! [`macro_rules!`][macro_rules] by using a special `pattern!` macro, and you can expand any pattern
369//! using the `expand!` macro:
370//!
371//! <div style="background-color:#397be440; padding: 8px; border-radius: 8px; margin-bottom: 8px;">
372//! π‘ Please note that the <code>expand!</code> macro simply passes its input along. It is used
373//! only to make the code within the function a valid Rust code block. Thus, you do not need to use
374//! it if you want to expand variables within other macros, like <code>stringify!</code>.
375//! </div>
376//!
377//! ```
378//! // Please note that we need to type the pattern argument as `_` to make the
379//! // code a valid Rust code.
380//! #[crabtime::function]
381//! fn gen_positions8(pattern!($name:ident, $components:tt): _) {
382//! let components = expand!($components);
383//! for dim in 1 ..= components.len() {
384//! let cons = components[0..dim].join(",");
385//! // We don't need to use `expand!` here.
386//! let name = stringify!($name);
387//! crabtime::output! {
388//! enum {{name}}{{dim}} {
389//! {{cons}}
390//! }
391//! }
392//! }
393//! }
394//! gen_positions8!(Position, ["X", "Y", "Z", "W"]);
395//! gen_positions8!(Color, ["R", "G", "B"]);
396//! # fn main() {}
397//! ```
398//!
399//! <br/>
400//!
401//! <h5><b>Input by using <code>TokenStream</code></b></h5>
402//!
403//! Alternatively, you can consume the provided input as a [TokenStream][token_stream]:
404//!
405//! ```
406//! #[crabtime::function]
407//! fn gen_positions9(name: TokenStream) {
408//! #![dependency(proc-macro2 = "1")]
409//! let components = ["X", "Y", "Z", "W"];
410//! let name_str = name.to_string();
411//! for dim in 1 ..= components.len() {
412//! let cons = components[0..dim].join(",");
413//! crabtime::output! {
414//! enum {{name_str}}{{dim}} {
415//! {{cons}}
416//! }
417//! }
418//! }
419//! }
420//! gen_positions9!(Position);
421//! # fn main() {}
422//! ```
423//!
424//! <br/>
425//! <br/>
426//!
427//! # π Performance
428//!
429//! The lifecycle of a Crabtime macro is similar to that of a procedural macro. It is compiled as a
430//! separate crate and then invoked to transform input tokens into output tokens. On the unstable
431//! Rust channel, Crabtime and procedural macros have the same performance. On the stable channel,
432//! Crabtime requires slightly more time than a procedural macro after you change your macro
433//! definition. In other words, Crabtimeβs performance is similar to procedural macros. It has
434//! higher compilation overhead than [`macro_rules!`][macro_rules] but processes tokens and complex
435//! transformations faster.
436//!
437//! | | <div style="width:200px">Proc Macro</div> | <div style="width:200px">Crabtime</div> | <div style="width:200px"><code>macro_rules!</code></div> |
438//! | :--- | :--- | :--- | :--- |
439//! | First evaluation (incl. compilation) | β οΈ Relatively slow | β οΈ Relatively slow | β
Fast |
440//! | Next evaluation (on call-site change) | β
Fast | β
Fast on nightly <br/> β οΈ Relatively slow on stable | β Slow for complex transformations |
441//! | Cost after changing module code without changing macro-call site code | β
Zero | β
Zero | β
Zero |
442//!
443//! <br/>
444//!
445//! <h5><b>Cache</b></h5>
446//!
447//! When a Crabtime macro is called, it creates a new Rust project, compiles it, evaluates it, and
448//! interprets the results as the generated Rust code. When you call the macro again (for example,
449//! after changing the macroβs parameters or calling the same macro in a different place), Crabtime
450//! can reuse the previously generated project. This feature is called βcaching.β It is enabled by
451//! default on the nightly channel and can be enabled on the stable channel by providing a `module`
452//! attribute, for example:
453//!
454//! ```ignore
455//! #[crabtime::function(cache_key=my_key)]
456//! #[module(my_crate::my_module)]
457//! fn my_macro() {
458//! // ...
459//! }
460//! ```
461//!
462//! The cache is always written to
463//! `<project_dir>/target/debug/build/crabtime/<module>/<macro_name>`. The defaults are presented
464//! below:
465//!
466//! | | Rust Unstable | Rust Stable |
467//! | :--- | :--- | :--- |
468//! | Cache enabled | β
| β by default, β
when `module` used. |
469//! | `module` default | path to def-site module | __none__ |
470//!
471//! Please note that caching will be automatically enabled on the stable channel as soon as the
472//! [proc_macro_span][proc_macro_span] feature is stabilized. That feature allows Crabtime to read
473//! the path of the file where the macro was used, so it can build a unique cache key.
474//!
475//! <br/>
476//!
477//! <h5><b>Performance Stats</b></h5>
478//!
479//! Crabtime also generates runtime and performance statistics to help you understand how much time
480//! was spent evaluating your macros, where projects were generated, and which options were used.
481//! If you expand any usage of `#[crabtime::function]` (for example, in your IDE), you will see
482//! compilation stats like:
483//!
484//! ```text
485//! # Compilation Stats
486//! Start: 13:17:09 (825)
487//! Duration: 0.35 s
488//! Cached: true
489//! Output Dir: /Users/crabtime_user/my_project/target/debug/build/crabtime/macro_path
490//! Macro Options: MacroOptions {
491//! cache: true,
492//! content_base_name: false,
493//! }
494//! ```
495//!
496//! Please note that you can be presented with the `Cached: true` result even after the first
497//! macro evaluation if your IDE or build system evaluated it earlier in the background.
498//!
499//! <br/>
500//! <br/>
501//!
502//! # πͺ² Logging & Debugging
503//!
504//! There are several ways to log from your Crabtime macros. Because
505//! [proc_macro::Diagnostic](https://doc.rust-lang.org/proc_macro/struct.Diagnostic.html) is
506//! currently a nightly-only feature, Crabtime prints nicer warnings and errors if you are using
507//! nightly Rust channel. They look just like warnings and errors from the Rust compiler.
508//! Otherwise, your warnings and errors will be printed to the console with a `[WARNING]` or
509//! `[ERROR]` prefix.
510//!
511//! | Method | Behavior on stable | Behavior on nightly |
512//! | :--- | :--- | :--- |
513//! | `println!` | Debug log in console | Debug log in console |
514//! | `crabtime::warning!` | Debug log in console | Warning in console |
515//! | `crabtime::error!` | Debug log in console | Error in console |
516//!
517//! <br/>
518//!
519//! <h5><b>Stdout Protocol</b></h5>
520//!
521//! Please note that Crabtime uses stdout for all communication between the code generation process
522//! and the host process. Depending on the prefix of each stdout line, it is interpreted according
523//! to the following table. In particular, instead of using the methods shown above, you can
524//! generate code from your macros by printing it to stdout (like
525//! `println!("[OUTPUT] struct T {}")`), but it's highly discouraged.
526//!
527//! | Prefix | Meaning |
528//! | :--- | :--- |
529//! | _(none)_ | Debug log message (informational output). |
530//! | `[OUTPUT]` | A line of generated Rust code to be included in the final macro output. |
531//! | `[WARNING]` | A compilation warning. |
532//! | `[ERROR]` | A compilation error. |
533//!
534//! <br/>
535//!
536//! <h5><b>Stdout Protocol Utilities</b></h5>
537//!
538//! Although you are not supposed to generate the Stdout Protocol messages manually, we believe that
539//! it is better to expose the underlying utilities so that in rare cases, you can use them to
540//! reduce the risk of malformed output. These functions allow you to transform multi-line strings
541//! by adding the appropriate prefixes:
542//!
543//! ```
544//! mod crabtime {
545//! fn prefix_lines_with(prefix: &str, input: &str) -> String {
546//! // Adds the given prefix to each line of the input string.
547//! # panic!()
548//! }
549//!
550//! fn prefix_lines_with_output(input: &str) -> String {
551//! // Adds `[OUTPUT]` to each line of the input string.
552//! # panic!()
553//! }
554//!
555//! fn prefix_lines_with_warning(input: &str) -> String {
556//! // Adds `[WARNING]` to each line of the input string.
557//! # panic!()
558//! }
559//!
560//! fn prefix_lines_with_error(input: &str) -> String {
561//! // Adds `[ERROR]` to each line of the input string.
562//! # panic!()
563//! }
564//! }
565//! ```
566//!
567//! These macros allow you to directly print prefixed lines to `stdout`, following the protocol:
568//!
569//! ```
570//! mod crabtime {
571//! macro_rules! output_str {
572//! // Outputs code by printing a line prefixed with `[OUTPUT]`.
573//! # () => {};
574//! }
575//!
576//! macro_rules! warning {
577//! // On the nightly channel prints a compilation warning.
578//! // On the stable channel prints a log prefixed with `[WARNING]`.
579//! # () => {};
580//! }
581//!
582//! macro_rules! error {
583//! // On the nightly channel prints a compilation error.
584//! // On the stable channel prints a log prefixed with `[ERROR]`.
585//! # () => {};
586//! }
587//! }
588//! ```
589//!
590//! <br/>
591//! <br/>
592//!
593//! # βοΈ Macro Cargo Configuration
594//!
595//! <div style="background-color:#397be440; padding: 8px; border-radius: 8px; margin-bottom: 8px;">
596//! π‘ On the Rust unstable channel, all configuration is automatically gathered from your
597//! Cargo.toml. It includes build-dependencies and code lints, including those defined in your
598//! workspace.
599//! </div>
600//!
601//! Every Crabtime macro is a separate Cargo project with its own configuration and dependencies.
602//! If you use nightly, Crabtime automatically uses your Cargo.toml configuration. On stable, due
603//! to lack of [proc_macro_span][proc_macro_span] stabilization, Crabtime cannot discover your
604//! Cargo.toml automatically. You must provide cargo configuration in your macro blocks, for
605//! example:
606//!
607//! ```
608//! #[crabtime::function]
609//! fn my_macro() {
610//! // Do this only on Rust stable channel. On the unstable channel
611//! // use your Cargo.toml's [build-dependencies] section instead.
612//! #![edition(2024)]
613//! #![resolver(3)]
614//! #![dependency(anyhow = "1.0")]
615//!
616//! type Result<T> = anyhow::Result<T>;
617//! // ...
618//! }
619//! # fn main() {}
620//! ```
621//!
622//! Crabtime recognizes these Cargo configuration attributes. The attributes below override any
623//! configuration discovered in your Cargo.toml, even on nightly:
624//!
625//! <br/>
626//!
627//! <h5><b>Supported Cargo Configuration Attributes</b></h5>
628//!
629//! | Attribute | Default |
630//! | :--- | :--- |
631//! | `#![edition(...)]` | 2024 |
632//! | `#![resolver(...)]` | 3 |
633//! | `#![dependency(...)]` | [] |
634//!
635//! <br/>
636//! <br/>
637//!
638//! # π Attributes
639//!
640//! You can provide any set of global attributes (`#![...]`) on top of your Crabtime macro
641//! definition for them to be applied to the given generated Crabtime crate.
642//!
643//! <br/>
644//! <br/>
645//!
646//! # πΊοΈ Paths
647//!
648//! Crabtime macros provide access to several path variables, allowing you to traverse your
649//! project's folder structure during macro evaluation. All paths are accessible within the
650//! `crabtime::` namespace.
651//!
652//!
653//! | Path | Availability | Description |
654//! | :--- | :--- | :--- |
655//! | `WORKSPACE_PATH` | Stable & Nightly | Path to the root of your project. This is where the top-most `Cargo.toml` resides, whether it's a single-crate project or a Cargo workspace. |
656//! | `CRATE_CONFIG_PATH` | Nightly only | Path to the `Cargo.toml` file of the current crate. |
657//! | `CALL_SITE_FILE_PATH` | Nightly only | Path to the file where the macro was invoked. |
658//!
659//!
660//! ```
661//! #[crabtime::function]
662//! fn check_paths() {
663//! println!("Workspace path: {}", crabtime::WORKSPACE_PATH);
664//! }
665//! check_paths!();
666//! # fn main() {}
667//! ```
668//!
669//! <br/>
670//! <br/>
671//!
672//! # π How It Works Under The Hood
673//!
674//! The content of a function annotated with `crabtime::function` is pasted into the `main`
675//! function of a temporary Rust project. This project is created, compiled, executed, and (if
676//! caching is disabled) removed at build time, and its `stdout` becomes the generated Rust code.
677//! The generated `main` function looks something like this:
678//!
679//! ```
680//! const SOURCE_CODE: &str = "..."; // Your code as a string.
681//!
682//! # mod phantom_for_crabtime_name_crash_resolution {
683//! mod crabtime {
684//! // Various utils described in this documentation.
685//! # pub fn push_as_str(str: &mut String, result: &()) {}
686//! # pub fn prefix_lines_with_output(input: &str) -> String { String::new() }
687//! }
688//!
689//! fn main() {
690//! let mut __output_buffer__ = String::new();
691//! let result = {
692//! // Your code.
693//! };
694//! crabtime::push_as_str(&mut __output_buffer__, &result);
695//! println!("{}", crabtime::prefix_lines_with_output(&__output_buffer__));
696//! }
697//! # }
698//! # fn main() {}
699//! ```
700//!
701//! The `output!` macro is essentially a shortcut for writing to output buffer using `format!`, so
702//! this:
703//!
704//! ```
705//! #[crabtime::function]
706//! fn my_macro_expansion1(components: Vec<String>) {
707//! for dim in 1 ..= components.len() {
708//! let cons = components[0..dim].join(",");
709//! crabtime::output! {
710//! enum Position{{dim}} {
711//! {{cons}}
712//! }
713//! }
714//! }
715//! }
716//! my_macro_expansion1!(["X", "Y", "Z", "W"]);
717//! # fn main() {}
718//! ```
719//!
720//! Is equivalent to:
721//!
722//! ```
723//! #[crabtime::function]
724//! fn my_macro_expansion2(pattern!([$($components_arg:expr),*$(,)?]): _) {
725//! let components: Vec<String> = expand!(
726//! [$(crabtime::stringify_if_needed!($components_arg).to_string()),*]
727//! ).into_iter().collect();
728//! for dim in 1 ..= components.len() {
729//! let cons = components[0..dim].join(",");
730//! crabtime::output_str! {"
731//! enum Position{dim} {{
732//! {cons}
733//! }}
734//! "}
735//! }
736//! }
737//! my_macro_expansion2!(["X", "Y", "Z", "W"]);
738//! # fn main() {}
739//! ```
740//!
741//! And that, in turn, is just the same as:
742//!
743//! ```
744//! #[crabtime::function]
745//! fn my_macro_expansion3() {
746//! let components = ["X", "Y", "Z", "W"];
747//! for dim in 1 ..= components.len() {
748//! let cons = components[0..dim].join(",");
749//! __output_buffer__.push_str(
750//! &format!("enum Position{dim} {{ {cons} }}\n")
751//! );
752//! }
753//! }
754//! my_macro_expansion3!();
755//! # fn main() {}
756//! ```
757//!
758//! Which, ultimately, is equivalent to:
759//!
760//! ```
761//! #[crabtime::function]
762//! fn my_macro_expansion4() {
763//! let components = ["X", "Y", "Z", "W"];
764//! for dim in 1 ..= components.len() {
765//! let cons = components[0..dim].join(",");
766//! println!("[OUTPUT] enum Position{dim} {{");
767//! println!("[OUTPUT] {cons}");
768//! println!("[OUTPUT] }}");
769//! }
770//! }
771//! my_macro_expansion4!();
772//! # fn main() {}
773//! ```
774//!
775//! <br/>
776//! <br/>
777//!
778//! # β οΈ Corner Cases
779//! There are a few things you should be aware of when using Crabtime:
780//! - Caching is associated with the current file path. It means that if in a single file you have
781//! multiple Crabtime macros of the same name (e.g. by putting them in different modules within a
782//! single file), they will use the same Rust project under the hood, which effectively breaks
783//! the whole purpose of caching.
784//! - You can't use Crabtime functions to generate consts. Instead, use `Crabtime::eval!` as shown
785//! above. This is because when expanding constants, macros need to produce an additional pair of
786//! `{` and `}` around the expanded tokens. If anyone knows how to improve this, please contact
787//! us.
788//! - Error spans from the generated code are not mapped to your source code. It means that you
789//! will still get nice, colored error messages, but the line/column numbers will be pointing to
790//! the generated file, not to your source file. This is an area for improvement, and I'd be
791//! happy to accept a PR that fixes this.
792//! - `Crabtime::eval!` does not use caching, as there is no name we can associate the cache with.
793//!
794//! <br/>
795//! <br/>
796//!
797//! # β οΈ Troubleshooting
798//!
799//! β οΈ **Note:** Rust IDEs differ in how they handle macro expansion. This macro is tuned for
800//! `rustc` and `RustRover`βs expansion engines.
801//!
802//! If your IDE struggles to correctly expand `crabtime::output!`, you can switch to the
803//! `crabtime::output_str!` syntax described above. If you encounter this, please
804//! [open an issue](https://github.com/wdanilo/eval-macro/issues) to let us know!
805//!
806//! [zigs_comptime]: https://zig.guide/language-basics/comptime
807//! [token_stream]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html
808//! [macro_fragments]: https://doc.rust-lang.org/reference/macros-by-example.html#metavariables
809//! [macro_rules]: https://doc.rust-lang.org/rust-by-example/macros.html
810//! [fn_like_macros]: https://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros
811//! [derive_macros]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros
812//! [attribute_macros]: https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros
813//! [proc_macro_span]: https://github.com/rust-lang/rust/issues/54725
814//! [rustfmt]: https://github.com/rust-lang/rustfmt
815//! [macro_hygiene]: https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
816//!
817//! [^supported_ides]: This code was thoroughly tested in `rustc`, the IntelliJ/RustRover Rust expansion engine, and Rust Analyzer (VS Code, etc.).
818//!
819//! [inline_dependency_injection]: ...
820//! [space_aware_interpolation]: ...
821#![cfg_attr(not(feature = "std"), no_std)]
822
823extern crate self as crabtime;
824pub use crabtime_internal::*;
825
826// =====================
827// === Macro Helpers ===
828// =====================
829
830#[macro_export]
831macro_rules! eval {
832 ($($ts:tt)*) => {
833 {
834 #[crabtime::eval_function(cache=true, content_base_name=true)]
835 fn run() -> _ {
836 $($ts)*
837 }
838 }
839 };
840}
841
842// ==========================
843// === Type Hints Mockups ===
844// ==========================
845// The following items are defined to prevent IDE error messages. The real definition is placed
846// in the generated project per macro usage.
847
848/// AVAILABLE ONLY WITHIN THE CRABTIME MACRO.
849#[macro_export]
850macro_rules! output {
851 ($($ts:tt)*) => {};
852}
853
854/// AVAILABLE ONLY WITHIN THE CRABTIME MACRO.
855#[macro_export]
856macro_rules! quote {
857 ($($ts:tt)*) => { String::new() };
858}
859
860/// AVAILABLE ONLY WITHIN THE CRABTIME MACRO.
861#[macro_export]
862macro_rules! write_ln {
863 ($($ts:tt)*) => {};
864}
865
866/// AVAILABLE ONLY WITHIN THE CRABTIME MACRO.
867///
868/// Returns all ordered combinations of positive integers that sum to `n` (with at least two
869/// summands). For example `sum_combinations(4)` returns
870///
871/// ```text
872/// [1, 1, 1, 1]
873/// [1, 1, 2]
874/// [1, 2, 1]
875/// [2, 1, 1]
876/// [1, 3]
877/// [3, 1]
878/// [2, 2]
879/// ```
880#[cfg(feature = "std")]
881#[allow(clippy::panic)]
882pub fn sum_combinations(_n: usize) -> Vec<Vec<usize>> {
883 panic!("AVAILABLE ONLY WITHIN THE CRABTIME MACRO.")
884}
885
886pub const WORKSPACE_PATH: &str = "AVAILABLE ONLY WITHIN THE CRABTIME MACRO.";
887pub const CRATE_CONFIG_PATH: &str = "AVAILABLE ONLY WITHIN THE CRABTIME MACRO.";
888pub const CALL_SITE_FILE_PATH: &str = "AVAILABLE ONLY WITHIN THE CRABTIME MACRO.";
889
890// =============
891// === Tests ===
892// =============
893
894/// Most of the tests are included in the documentation above. These tests cover corner cases to
895/// ensure that the macro works as expected.
896#[cfg(all(test, feature = "std"))]
897mod tests {
898 #[test]
899 fn empty_def_compilation() {
900 #[crabtime::function]
901 fn empty_def_compilation() {}
902 empty_def_compilation!();
903 }
904
905 // ===
906
907 mod mod_a {
908 #[crabtime::function]
909 fn inter_module_macro() -> &str {
910 "pub struct Generated;"
911 }
912 #[allow(clippy::single_component_path_imports)]
913 pub(super) use inter_module_macro;
914 }
915
916 mod mod_b {
917 super::mod_a::inter_module_macro!();
918 }
919
920 #[test]
921 fn inter_module_macro() {
922 let _p = mod_b::Generated;
923 }
924
925 #[test] fn interpolation_before_brace() {
926 #[crabtime::function]
927 fn interpolation_before_brace() {
928 let is_a_branches = "A => true, B => false";
929 crabtime::output! {
930 enum E { A, B }
931 impl E {
932 fn is_a(&self) -> bool {
933 match self {
934 {{is_a_branches}}
935 }
936 }
937 }
938 }
939 }
940 interpolation_before_brace!();
941 }
942
943 // ===
944
945 // https://github.com/wdanilo/crabtime/issues/25
946 mod test_impl_interpolation_compilation {
947 #[crabtime::function]
948 fn fn_in_impl() -> &str {
949 "pub fn test(&self) {}"
950 }
951 struct Test;
952 impl Test {
953 fn_in_impl!();
954 }
955 }
956}