moxy_template/lib.rs
1mod ast;
2
3use ast::{Paste, Template};
4use moxy_token::{ToTokens, TokenStream};
5
6/// Build a [`moxy::token::TokenStream`] at runtime from a template, in the style
7/// of `quote!`.
8///
9/// The macro expands to a block that constructs and returns a `TokenStream`.
10/// Interpolations and control flow are evaluated against the surrounding scope:
11///
12/// - `{{ expr }}` splices the runtime value of `expr` (via [`moxy::token::ToTokens`]),
13/// preserving its source spans.
14/// - `@for (binding in iter) { … }`, `@if (cond) { … } @else { … }`, and
15/// `@match (expr) { pat => { … }, … }` run as real control flow, appending to
16/// the output as they execute.
17///
18/// # Example
19///
20/// ```ignore
21/// let items = vec!["a", "b", "c"];
22/// let tokens = template! {
23/// @for (item in items) {
24/// {{ item }}
25/// }
26/// };
27/// assert_eq!(tokens.to_string(), "a b c");
28/// ```
29///
30/// A malformed template produces a span-targeted compile error.
31#[proc_macro]
32pub fn template(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
33 let mut ts = TokenStream::new();
34 input.to_tokens(&mut ts);
35
36 let expanded = match ts.parse().parse::<Template>() {
37 Ok(tmpl) => tmpl.expand(),
38 Err(e) => e.to_compile_error(),
39 };
40
41 let mut out = proc_macro::TokenStream::new();
42 expanded.to_tokens(&mut out);
43 out
44}
45
46/// Concatenates the tokens inside each `{{ ... }}` marker into a single
47/// identifier at compile time, passing all other tokens through unchanged.
48///
49/// Use it to mint identifier *names* in declaration position — something the
50/// runtime [`moxy::token::ident!`] macro cannot do. Segments are taken by token
51/// text (not evaluated), then validated as a Rust identifier.
52///
53/// # Example
54///
55/// ```ignore
56/// paste! {
57/// fn {{ get_ value }}() -> u32 { 7 } // expands to: fn get_value() -> u32 { 7 }
58/// }
59/// assert_eq!(get_value(), 7);
60/// ```
61///
62/// A `{{ ... }}` that does not concatenate to a valid identifier produces a
63/// span-targeted compile error.
64#[proc_macro]
65pub fn paste(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
66 let mut ts = TokenStream::new();
67 input.to_tokens(&mut ts);
68
69 let expanded = match ts.parse().parse::<Paste>() {
70 Ok(p) => p.expand(),
71 Err(e) => e.to_compile_error(),
72 };
73
74 let mut out = proc_macro::TokenStream::new();
75 expanded.to_tokens(&mut out);
76 out
77}