Rust syntax tools for procedural macros: tokens, typed syntax trees, templates, formatting, and diagnostics.
cargo add moxy --features template,fmtuse moxy::ast::Item;
let name = "Widget";
let tokens = moxy::template! {
pub struct {{ name }};
};
let item: Item = moxy::parse!(tokens).unwrap();
assert_eq!(item.as_struct().unwrap().ident.text(), "Widget");
assert_eq!(moxy::fmt!(&item).unwrap(), "pub struct Widget;");Default features are token and ast.
| Feature | Default | Enables |
|---|---|---|
token |
yes | Token streams, spans, parsing, and token construction |
ast |
yes | Typed Rust syntax trees; implies token |
template |
no | template! and paste!; implies token |
fmt |
no | AST formatting with fmt!; implies ast |
diagnostic |
no | Span-aware error, warning, note, and help diagnostics |
build |
no | Cargo build-script and rustc-version helpers |
macros |
no | #[derive(ToTokens)], #[derive(FromMeta)], and macro-authoring attributes |
derives |
no | Standard trait derives for supported AST and template types |
serde |
no | Serialization for supported token, AST, and formatting types |
proc-macro2 |
no | Conversions between moxy and proc_macro2 tokens |
full |
no | Every feature above |
Choose only the layers you need:
cargo add moxy --no-default-features --features token,astThe token feature is the foundation, similar in role to proc-macro2.
use moxy::Token;
use moxy::token::ident;
let name = ident!(Generated, "_", Item);
let comma: Token![,] = Default::default();
assert_eq!(name.to_string(), "Generated_Item");
assert_eq!(comma.as_str(), ",");The ast feature provides typed entry points such as Item, Expr, and
Type, following the same parse-at-the-level-you-need style as syn.
use moxy::ast::{Expr, Item, Type};
let item: Item = moxy::parse!("pub struct User { id: u64 }").unwrap();
let ty: Type = moxy::parse!("Option<Result<T, E>>").unwrap();
let expr: Expr = moxy::parse!("items.next()?").unwrap();
assert!(item.is_struct());
assert!(ty.is_path());
assert!(expr.is_unary());The template feature builds token streams with interpolation and control
flow in the style of quote!.
let fields = ["id", "name"];
let tokens = moxy::template! {
struct User {
@for field in fields {
{{ field }}: String,
}
}
};
assert!(tokens.to_string().contains("struct User"));paste! creates identifiers at expansion time:
moxy::paste! {
fn {{ read_ value }}() -> u32 { 7 }
}
assert_eq!(read_value(), 7);Template control flow accepts ordinary Rust bindings and may omit the header
parentheses. @if let and destructuring @for bindings work directly, while
@match arms accept Rust patterns and guards:
let value = Some("enabled");
let fields = [("name", "String")];
let tokens = moxy::template! {
@if let Some(value) = value { const ENABLED: &str = {{ value }}; }
struct Config { @for (name, ty) in &fields { {{ name }}: {{ ty }}, } }
};Interpolations can also use a Rust statement block when their final expression is the value to emit:
let tokens = moxy::template! {
{{
let value = "generated".to_string();
value
}}
};The fmt feature formats parsed syntax trees with configurable width,
indentation, and newlines.
use moxy::ast::Item;
use moxy::fmt::{FmtConfig, Indent};
let item: Item = moxy::parse!("struct User { id: u64, name: String }").unwrap();
let config = FmtConfig::default().with_indent(Indent::space(2));
let output = moxy::fmt!(&item, config).unwrap();
assert_eq!(output, "struct User {\n id: u64,\n name: String,\n}");The diagnostic feature builds span-aware diagnostics with a stable
compile_error! fallback.
let tokens = moxy::error!(
"missing template",
[moxy::help!("add #[template { ... }]")],
)
.emit();
assert!(tokens.to_string().contains("compile_error"));Enable macros to implement ToTokens from an inline template.
cargo add moxy --features macrosuse moxy::token::ToTokenStream;
#[derive(moxy::ToTokens)]
#[moxy(template { const VALUE: &str = {{ self.value }}; })]
struct Generated {
value: String,
}
let tokens = Generated { value: "seven".into() }.to_token_stream();
assert!(tokens.to_string().contains("VALUE"));On nightly Rust, add #[moxy(debug)] beside #[moxy(template { ... })] to
print the parsed declaration and generated implementation as compiler notes.
Stable Rust does not emit these debug notes.
#[derive(moxy::FromMeta)] converts structured attributes into a named struct
or enum. Call Attributed::parse_meta
on an AST node to parse the first matching attribute. The derive adds
FromMeta bounds for generic type parameters.
use moxy::ast::Attributed;
#[derive(moxy::FromMeta)]
struct BuildArgs {
// Field names are meta-item names unless renamed.
// `default` uses `None` when `rename` is absent.
#[meta(default)]
rename: Option<String>,
// Parse `default` into `is_default`; use `default = expr` for a custom fallback.
#[meta(rename = "default", default)]
is_default: bool,
// Customize the error for this required value.
#[meta(message = "missing build name")]
name: String,
}
fn build_args(field: &moxy::ast::Field) -> Result<Option<BuildArgs>, moxy::ast::ParseError> {
// Parse the first `#[build(...)]` attribute on this field.
field.parse_meta("build")
}Use #[moxy::derive(Name)] to turn a public function that accepts one
parseable moxy AST type into a custom derive macro. The derive name is the
identifier in the attribute; the function name remains an implementation
detail.
use moxy::ast::{ItemStruct, ParseError};
use moxy::token::TokenStream;
#[moxy::derive(Builder)]
pub fn builder(item: ItemStruct) -> Result<TokenStream, ParseError> {
Ok(moxy::template! {
impl {{ item.ident }} {
pub fn builder() -> Self { todo!() }
}
})
}Downstream users invoke the generated derive normally:
#[derive(Builder)]
struct Config;The annotated function must be public, take exactly one parameter, and return
Result<TokenStream, ParseError>. Its parameter may be any moxy type that
implements Parse: ItemStruct limits the derive to structs, ItemEnum
limits it to enums, and other syntax types work the same way. Use
moxy::ast::Declaration when the derive accepts any declaration and chooses
the supported variants itself.
#[moxy::derive(EnumName)]
pub fn enum_name(item: moxy::ast::ItemEnum) -> Result<moxy::token::TokenStream, moxy::ast::ParseError> {
Ok(moxy::template! {
impl {{ item.ident }} {
pub const NAME: &'static str = stringify!({{ item.ident }});
}
})
}See the complete examples/builder implementation and its
consumer test for a builder derive that accepts Declaration and explicitly
checks for a struct.
Enable macros to turn a public token-to-token function into a function-like
procedural macro with #[moxy::function]. The function accepts one
TokenStream argument and returns Result<TokenStream, ParseError>.
use moxy::ast::ParseError;
use moxy::token::TokenStream;
#[moxy::function(name = "world_hello")]
pub fn render(_tokens: TokenStream) -> Result<TokenStream, ParseError> {
Ok(moxy::template! {
println!("world, hello")
})
}
world_hello!();Omit name = "…" to export a macro with the annotated function's name.
On nightly Rust, add debug (for example, #[moxy::function(debug)]) to emit
the generated wrapper as a compiler note. Stable Rust does not emit this note.
Enable macros to turn a public function taking the attribute arguments and
annotated item into an attribute procedural macro with #[moxy::attribute].
use moxy::ast::ParseError;
use moxy::token::TokenStream;
#[moxy::attribute(name = "passthrough")]
pub fn expand(_meta: TokenStream, item: TokenStream) -> Result<TokenStream, ParseError> {
Ok(item)
}
#[passthrough]
fn generated() {}The function receives the attribute arguments first and the annotated item
second. Omit name = "…" to export an attribute with the function's name.
On nightly Rust, add debug (for example, #[moxy::attribute(debug)]) to emit
the generated wrapper as a compiler note. Stable Rust does not emit this note.
serde adds serialization for supported token, AST, and formatter types.
proc-macro2 adds token conversions for interoperability with the wider
procedural-macro ecosystem.