| Crates.io | pud-macros |
| lib.rs | pud-macros |
| version | 1.0.0 |
| created_at | 2025-12-21 20:26:16.35989+00 |
| updated_at | 2025-12-27 14:24:31.713479+00 |
| description | Generate typed, composable, no-std-friendly modifications (“puds”) for Rust structs. |
| homepage | https://github.com/vic1707/pud |
| repository | https://github.com/vic1707/pud |
| max_upload_size | |
| id | 1998537 |
| size | 23,947 |
pud is a procedural macro and trait system for generating typed, composable, no-std-friendly modifications (“puds”) for Rust structs.
Pud]A pud is a single atomic modification that can be applied to a target value.
pub trait Pud {
type Target;
fn apply(self, target: &mut Self::Target);
}
A pud consumes itself and mutates its Target in place.
Pudded]Any sized type can receive puds (blanket implementation).
pub trait Pudded: Sized {
fn apply(&mut self, pud: impl Pud<Target = Self>);
fn apply_batch(&mut self, puds: impl Iterator<Item = impl Pud<Target = Self>>);
}
IntoPud] / [TryIntoPud]IntoPud (and its fallible equivalent TryIntoPud) allows external code to produce a modification without depending on the concrete {StructName}Pud enum generated by #[pud].
This is useful for update producers such as commands, events, or protocol messages, which should be able to request changes to a struct without knowing the name, visibility, or exact shape of its Pud enum.
pub trait IntoPud {
type Pud: Pud;
fn into_pud(self) -> Self::Pud;
}
pub trait TryIntoPud {
type Pud: Pud;
type Error;
fn try_into_pud(self) -> Result<Self::Pud, Self::Error>;
}
TryIntoPud is the fallible variant and is automatically implemented for all IntoPud types using Infallible as the error.
#[pud] macroThe #[pud] attribute is applied to a struct and generates:
A Pud enum named {StructName}Pud (by default)
An implementation of Pud<Target = StructName> for that enum
Optional visibility, attributes, and renaming control
Note: The generated code is fully #![no_std] compatible and doesn't depend on alloc.
#[::pud::pud]
pub struct Foo {
a: u8,
b: u8,
}
Generates:
pub struct Foo {
a: u8,
b: u8,
}
pub enum FooPud {
A(u8),
B(u8),
}
#[automatically_derived]
impl ::pud::Pud for FooPud {
type Target = Foo;
fn apply(self, target: &mut Self::Target) {
match self {
Self::A(_0) => {
target.a = _0;
}
Self::B(_1) => {
target.b = _1;
}
}
}
}
Struct-level settings are provided inside the #[pud(...)] attribute.
#[pud(
vis = pub(crate),
attrs(
repr(C),
derive(Debug)
),
rename = CustomPudName
)]
| Setting | Description |
|---|---|
vis = <visibility> |
Visibility of the generated Pud enum |
rename = <Ident> |
Rename the generated Pud enum |
attrs(...) |
Attributes applied to the generated Pud enum |
Field settings control how individual fields participate in the Pud enum and how updates are applied.
Settings may be comma-separated or split across multiple #[pud(...)] attributes.
rename = IdentRenames the generated Pud variant.
#[pud(rename = FOO)]
foo: u8,
Generates
FooPud::FOO(u8) // instead of FooPud::foo(u8)
map(Type >>= expr)Allows you to use a mapper instead of the field type, mapper can be a function path or a closure expression (Fn(Type) -> field_type)
#[pud(map(u8 >>= u8_to_u16))]
toto: u16,
Generates:
target.toto = u8_to_u16(_0);
flatten = TypeDelegates modification to another Pud type (a glorified map(InnerPud >>= Pud::apply)).
#[pud(flatten = AnotherPud)]
titi: u8,
group = IdentGroups multiple fields into a single multi-field Pud variant.
#[pud(group = FooBarBaz)]
foo: u8,
#[pud(group = FooBarBaz)]
bar: u8,
#[pud(group = FooBarBaz)]
baz: u8,
Generates:
FooBarBaz(u8, u8, u8) // and appropriate application
Note: Groups do not remove the individual field variants; both coexist.
#[::pud::pud(
vis = pub(crate),
attrs(
repr(C),
derive(Debug)
),
)]
#[derive(Debug)]
pub struct Foo {
#[pud(map(u8 >>= u8_to_u16))]
toto: u16,
#[pud(rename = TATA)]
tata: u8,
#[pud(flatten = AnotherPud)]
titi: u8,
#[pud(group = FooBarBaz)]
foo: u8,
#[pud(group = FooBarBaz)]
bar: u8,
#[pud(group = FooBarBaz)]
baz: u8,
}
Generates:
#[derive(Debug)]
pub struct Foo {
toto: u16,
tata: u8,
titi: u8,
foo: u8,
bar: u8,
baz: u8,
}
#[repr(C)]
#[derive(Debug)]
pub(crate) enum FooPud {
Toto(u8),
TATA(u8),
Titi(AnotherPud),
Foo(u8),
Bar(u8),
Baz(u8),
FooBarBaz(u8, u8, u8),
}
#[automatically_derived]
impl ::pud::Pud for FooPud {
type Target = Foo;
fn apply(self, target: &mut Self::Target) {
match self {
Self::Toto(_0) => { target.toto = (u8_to_u16)(_0); }
Self::TATA(_1) => { target.tata = _1; }
Self::Titi(_2) => { _2.apply(&mut target.titi); }
Self::Foo(_3) => { target.foo = _3; }
Self::Bar(_4) => { target.bar = _4; }
Self::Baz(_5) => { target.baz = _5; }
Self::FooBarBaz(_3, _4, _5) => {
target.foo = _3;
target.bar = _4;
target.baz = _5;
}
}
}
}
pud is minimal and explicit: a pud describes what to update, not whether or how to update it.
Producing a modification is a deliberate act—if a value should not change, ideally, no pud should be emitted.
The crate avoids side effects, hidden control flow, or mutable access outside of apply; mapping is pure, and application is mechanical and predictable.
Conditional or state-dependent updates are outside the core design, but may be added later via optional, feature-gated extensions without affecting the current guarantees.
Only structs are supported.
Named and tuple structs are allowed; tuple struct fields must be explicitly renamed.