misses init command with binary and library scaffold modes
`misses init [--lib] [dir]` scaffolds a new proc-macro crate that uses misses as its build toolchain. Two modes are supported: Binary mode (default): The .mrs source files live in src/macros/ and are compiled to $OUT_DIR at build time via a generated build.rs. The generated Rust code is never committed — users must have misses installed to build from source. Library mode (--lib): The generated code is committed to generated/ so downstream users of the crate need no misses install at all. The .mrs source and build.rs are still present for the library author, who can regenerate via `cargo build --features misses-source`. The misses-source feature makes misses an optional build-dependency and gates the regeneration path in build.rs with #[cfg(feature)]. The command also teaches `main.rs` a proper subcommand dispatch so that `misses <file.mrs>` (compile) and `misses init` are cleanly separated. https://claude.ai/code/session_01D2g6zGJsBeUEzxBb7AvFDt
Misses
Misses is a DSL compiler that transforms .mrs macro definitions into ready-to-use Rust proc macro source files. Write your macro once in a concise, readable syntax; Misses generates the full syn/quote boilerplate for you.
Quick start
# Build everything
cargo build
# Run a bundled example (compare .mrs source with generated Rust)
cargo run --example make_id_type
cargo run --example derive_builder
cargo run --example derive_getters
cargo run --example declare_table
cargo run --example component
# Compile a .mrs file to Rust
cargo run --bin misses -- path/to/my_macro.mrs > src/generated.rs
The .mrs syntax
A .mrs file contains one or more macro definitions:
macro macro_name! {
<pattern> => {
<output>
}
}
Pattern bindings
| Syntax | Parsed as |
|---|---|
$name:ident |
A single identifier |
$expr:expr |
Any Rust expression |
$ty:ty |
A Rust type |
$body:tt |
A raw token-tree (brace group) |
$($field:ident : $ty:ty),* |
Variadic — zero-or-more comma-separated pairs |
Literal tokens in the pattern are matched verbatim. Rust keywords use syn::Token![…]; arbitrary identifiers are matched by value.
Output blocks
An output block may contain any mix of:
Escape block @{ … } — arbitrary Rust code executed at macro-expansion time:
@{
let builder_name = format_ident!("{}_Builder", name.to_string());
}
Token template T~{ … } — generates the quote::quote! { … } call. Inside a template you may use:
| Syntax | Meaning |
|---|---|
$name |
Interpolate the bound variable name |
$field.name / $field.ty |
Access a field of a variadic element |
${ expr } |
Evaluate an arbitrary Rust expression and interpolate the result |
for x in $xs => … |
Spread — iterate the variadic collection and repeat a line |
#[a => b_$name] |
Construct an identifier from parts (uses format_ident!) |
Examples
make_id_type.mrs — newtype ID wrapper
macro make_id_type! {
$name:ident => {
T~{
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct $name(pub u64);
impl $name {
pub fn new(val: u64) -> Self { Self(val) }
pub fn value(self) -> u64 { self.0 }
}
}
}
}
derive_builder.mrs — builder pattern
macro derive_builder! {
struct $name:ident { $($field:ident : $ty:ty),* } => {
@{
let builder_name = format_ident!("{}_Builder", name.to_string());
}
T~{
pub struct ${ builder_name } {
for field in $fields =>
pub $field.name: Option<$field.ty>,
}
impl ${ builder_name } {
pub fn build(self) -> $name {
$name {
for field in $fields =>
$field.name: self.$field.name.unwrap(),
}
}
}
}
}
}
declare_table.mrs — DSL to struct with runtime helper
macro declare_table! {
$name:ident { $body:tt } => {
@{
let table_str = misses_rt::to_table_name(&name);
}
T~{
pub struct $name {
pub id: u64,
}
impl $name {
pub const TABLE: &'static str = ${ table_str.as_str() };
}
}
}
}
The misses-rt runtime library
misses-rt provides helpers that generated proc macro code can call at expansion time. Add it to your proc macro crate:
[dependencies]
misses-rt = { path = "../misses-rt" }
Name helpers
misses_rt::to_table_name(&ident) // UsersAccount → "users_accounts"
misses_rt::to_snake_case(&ident) // MyType → "my_type"
misses_rt::to_snake_case_str(s) // "MyType" → "my_type"
misses_rt::to_pascal_case(s) // "my_type" → MyType (Ident)
misses_rt::concat_ident(&[a, b]) // ["foo", "Bar"] → fooBar (Ident)
Type helpers
misses_rt::is_option(&ty) // Option<T> → true
misses_rt::unwrap_option(&ty) // Option<T> → Some(&T)
misses_rt::to_sql_type(&ty) // syn::Type → "TEXT", "INTEGER", …
HTML template helpers
let mut buf = misses_rt::Buffer::new();
buf.push_str("<b>");
buf.push_escaped("<script>"); // → "<script>"
let markup: misses_rt::Markup = misses_rt::Markup::from_buffer(buf);
println!("{}", markup.into_string());
Section / key-value parsing
// Parse `props { … } slots { … }` blocks from a token stream
let map = misses_rt::parse_sections(tokens, &["props", "slots"]);
let props_tokens = map.get("props");
// Parse `key = value, …` pairs
let kv = misses_rt::parse_kv(tokens);
let val = kv.get("name");
Project layout
misses/
├── misses-compiler/ # The .mrs → Rust transpiler
│ ├── src/
│ │ ├── ast.rs # Full AST types
│ │ ├── parser.rs # Hand-written recursive descent parser
│ │ ├── codegen.rs # AST → Rust source string
│ │ ├── lib.rs # Library API (parse + codegen)
│ │ └── main.rs # CLI binary
│ └── examples/ # Runnable .mrs demonstrations
│ ├── make_id_type.{mrs,rs}
│ ├── derive_builder.{mrs,rs}
│ ├── derive_getters.{mrs,rs}
│ ├── declare_table.{mrs,rs}
│ └── component.{mrs,rs}
└── misses-rt/ # Runtime helpers for generated proc macros
└── src/
├── lib.rs # Buffer, Markup, name/type helpers
├── parse_helpers.rs
└── template.rs # HTML template AST + code-gen
How code generation works
For each .mrs macro, the compiler generates:
- A
#[proc_macro]function that receivesproc_macro::TokenStream. - Parser statements that destructure the input according to the pattern.
- Any
@{}escape-block statements, executed inline. - A
quote::quote! { … }call whose body mirrors theT~{}template, with spread iterators for variadic bindings. - The
quote!result is returned asproc_macro::TokenStream.
The generated file is formatted with prettyplease for readable output.