Skip to main content

moxy_template/
lib.rs

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