nami-derive

Crates.ionami-derive
lib.rsnami-derive
version0.2.0
created_at2025-08-31 15:20:16.927538+00
updated_at2025-09-06 15:55:49.640377+00
descriptionDerive macros for the nami reactive framework
homepagehttps://github.com/water-rs/nami
repositoryhttps://github.com/water-rs/nami
max_upload_size
id1818633
size24,846
Lexo Liu (lexoliu)

documentation

https://docs.rs/nami-derive

README

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 the Project trait 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 Clone and have 'static lifetime
  • Generic parameters automatically get 'static bounds 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);
Commit count: 27

cargo fmt