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}