# 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 ```bash # Install the CLI cargo install misses # Scaffold a new proc-macro crate (binary mode) misses init my-macros # Scaffold a library-friendly crate (generated code committed, no misses required downstream) misses init --lib my-macros # Compile a .mrs file to Rust misses path/to/my_macro.mrs > src/generated.rs misses path/to/my_macro.mrs src/generated.rs ``` --- ## `misses init` — project scaffolding `misses init` creates a ready-to-build proc-macro crate. Two modes are available depending on whether you're shipping a binary or a library. ### Binary mode (default) ```bash misses init my-macros ``` Use this when you control the build environment (e.g. an application binary). The `.mrs` sources are compiled to Rust at build time via `build.rs`; the generated code lives in Cargo's `$OUT_DIR` and is **never committed**. ``` my-macros/ ├── Cargo.toml # misses as a plain [build-dependencies] ├── build.rs # compiles src/macros/*.mrs → $OUT_DIR/*.rs └── src/ ├── lib.rs # include!(concat!(env!("OUT_DIR"), "/hello.rs")) └── macros/ └── hello.mrs # your macro source (committed) ``` Users only need `misses` installed to build from source, which is fine for an app you control. ### Library mode (`--lib`) ```bash misses init --lib my-macros ``` Use this when you're publishing a library crate. Downstream users of your crate should not be forced to install misses. The generated Rust code is committed to `generated/` and shipped on crates.io. ``` my-macros/ ├── Cargo.toml # misses behind [features] misses-source = ["dep:misses"] ├── build.rs # no-op unless --features misses-source ├── generated/ │ └── hello.rs # pre-generated, committed to git └── src/ ├── lib.rs # include!(concat!(env!("CARGO_MANIFEST_DIR"), "/generated/hello.rs")) └── macros/ └── hello.mrs # your macro source (committed) ``` When you modify a `.mrs` file, regenerate with: ```bash cargo build --features misses-source ``` The `misses-source` feature makes `misses` an optional build-dependency and gates regeneration in `build.rs`. Downstream users build normally with no misses dependency. --- ## The `.mrs` syntax A `.mrs` file contains one or more macro definitions: ``` macro macro_name! { => { } } ``` ### 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() }; } } } } ``` Run the bundled examples to compare `.mrs` source with generated Rust: ```bash 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 ``` --- ## 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: ```toml [dependencies] misses-rt = { path = "../misses-rt" } ``` ### Name helpers ```rust 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 ```rust misses_rt::is_option(&ty) // Option → true misses_rt::unwrap_option(&ty) // Option → Some(&T) misses_rt::to_sql_type(&ty) // syn::Type → "TEXT", "INTEGER", … ``` ### HTML template helpers ```rust let mut buf = misses_rt::Buffer::new(); buf.push_str(""); buf.push_escaped("