Skip to main content

moxy_template/
lib.rs

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