| Crates.io | nami-derive |
| lib.rs | nami-derive |
| version | 0.2.0 |
| created_at | 2025-08-31 15:20:16.927538+00 |
| updated_at | 2025-09-06 15:55:49.640377+00 |
| description | Derive macros for the nami reactive framework |
| homepage | https://github.com/water-rs/nami |
| repository | https://github.com/water-rs/nami |
| max_upload_size | |
| id | 1818633 |
| size | 24,846 |
Derive and helper macros for the nami reactive framework.
This crate provides procedural macros to make reactive code more ergonomic, including
field projection for structs and an s! macro that builds formatted string signals.
#[derive(Project)] - Automatically implement the Project trait for structs, enabling decomposition into individual field bindingsAdd this to your Cargo.toml:
[dependencies]
nami = { version = "0.3", features = ["derive"] }
The macros are re-exported through the main nami crate when the derive feature is enabled (default).
#[derive(Project)]The Project derive macro automatically generates implementations that allow you to decompose struct bindings into separate bindings for each field.
For structs with named fields, the macro generates a corresponding "projected" struct where each field is wrapped in a Binding:
use nami::{Binding, binding};
#[derive(nami::Project)]
struct Person {
name: String,
age: u32,
email: String,
}
let person: Binding<Person> = binding(Person {
name: "Alice".to_string(),
age: 30,
email: "alice@example.com".to_string(),
});
// Project into individual field bindings
let projected = person.project();
// Access and modify individual fields
projected.name.set("Bob".to_string());
projected.age.set(25);
// Changes are reflected in the original binding
let updated_person = person.get();
assert_eq!(updated_person.name, "Bob");
assert_eq!(updated_person.age, 25);
assert_eq!(updated_person.email, "alice@example.com"); // unchanged
For tuple structs, the macro generates a tuple of bindings:
use nami::{Binding, binding};
#[derive(nami::Project)]
struct Point(i32, i32);
let point: Binding<Point> = binding(Point(10, 20));
let (x, y) = point.project();
x.set(100);
y.set(200);
assert_eq!(point.get().0, 100);
assert_eq!(point.get().1, 200);
Unit structs project to the unit type ():
use nami::{Binding, binding};
#[derive(nami::Project)]
struct Marker;
let marker: Binding<Marker> = binding(Marker);
let _unit = marker.project(); // Returns ()
The derive macro supports generic types with appropriate lifetime bounds:
use nami::{Binding, binding};
#[derive(nami::Project)]
struct Container<T> {
value: T,
count: usize,
}
let container: Binding<Container<String>> = binding(Container {
value: "hello",
count: 5,
});
let projected = container.project();
projected.value.set("world");
projected.count.set(10);
All projected bindings maintain bidirectional reactivity with the original binding:
Clone and have 'static lifetime'static bounds addedThis project is licensed under the MIT License - see the LICENSE file for details.
s! macroCreates a formatted string signal, automatically capturing named variables from the format string.
use nami::*;
let name = constant("Alice");
let age = constant(25);
// Automatic variable capture from format string
let msg = s!("Hello {name}, you are {age} years old");
// Positional arguments still work
let msg2 = s!("Hello {}, you are {}", name, age);