5 releases
| 0.2.3 | Jan 23, 2026 |
|---|---|
| 0.2.2 | Dec 5, 2025 |
| 0.2.1 | Oct 22, 2025 |
| 0.2.0 | Sep 6, 2025 |
| 0.1.0 | Aug 31, 2025 |
#39 in #binding
79 downloads per month
Used in nami
25KB
421 lines
nami-derive
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.
Features
#[derive(Project)]- Automatically implement theProjecttrait for structs, enabling decomposition into individual field bindings
Usage
Add 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.
Named Structs
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
Tuple Structs
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
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 ()
Generic Types
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);
Bidirectional Reactivity
All projected bindings maintain bidirectional reactivity with the original binding:
- Changes to projected bindings update the corresponding field in the original
- Changes to the original binding are reflected in the projected bindings
- The reactive system ensures efficient updates and notifications
Limitations
- The derive macro only supports structs (not enums or unions)
- All field types must implement
Cloneand have'staticlifetime - Generic parameters automatically get
'staticbounds added
License
This project is licensed under the MIT License - see the LICENSE file for details.
s! macro
Creates 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);
Dependencies
~165–580KB
~14K SLoC