Expand description
§pud
pud is a procedural macro and trait system for generating typed, composable, no-std-friendly modifications (“puds”) for Rust structs.
§Core Concepts
§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.
§The #[pud] macro
The #[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.
§Basic Example
#[::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
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-Level Settings
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 = Ident
Renames 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 = Type
Delegates modification to another Pud type (a glorified map(InnerPud >>= Pud::apply)).
#[pud(flatten = AnotherPud)]
titi: u8,§group = Ident
Groups 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 applicationNote: Groups do not remove the individual field variants; both coexist.
§Full Example
#[::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;
}
}
}
}§Design Philosophy
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.
§Supported Types and Constraints
Only structs are supported.
Named and tuple structs are allowed; tuple struct fields must be explicitly renamed.
Traits§
- IntoPud
- Conversion into a
Pudtargeting a specific type. - Pud
- A single atomic modification that can be applied to a target type.
- Pudded
- A type that can have
Puds applied to it. - TryInto
Pud - Fallible version of into
IntoPud.