# pud
[<img alt="github" src="https://img.shields.io/badge/github-vic1707/pud-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/vic1707/pud)
[<img alt="crates.io" src="https://img.shields.io/crates/v/pud.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/pud)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-pud-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/pud)
[<img alt="lines" src="https://www.aschey.tech/tokei/github.com/vic1707/pud?label=&style=for-the-badge&logo=https://simpleicons.org/icons/rust.svg?logoAsLabel%3D1?category%3Dcode" height="20">](https://github.com/vic1707/pud)
[<img alt="maintenance" src="https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg?style=for-the-badge" height="20">](https://github.com/vic1707/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.
```rs
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 `pud`s (blanket implementation).
```rs
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.
```rs
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
```rs
#[::pud::pud]
pub struct Foo {
a: u8,
b: u8,
}
```
Generates:
```rs
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.
```rs
#[pud(
vis = pub(crate),
attrs(
repr(C),
derive(Debug)
),
rename = CustomPudName
)]
```
| `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.
```rs
#[pud(rename = FOO)]
foo: u8,
```
Generates
```rs
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`)
```rs
#[pud(map(u8 >>= u8_to_u16))]
toto: u16,
```
Generates:
```rs
target.toto = u8_to_u16(_0);
```
##### `flatten = Type`
Delegates modification to another Pud type (a glorified `map(InnerPud >>= Pud::apply)`).
```rs
#[pud(flatten = AnotherPud)]
titi: u8,
```
##### `group = Ident`
Groups multiple fields into a single multi-field Pud variant.
```rs
#[pud(group = FooBarBaz)]
foo: u8,
#[pud(group = FooBarBaz)]
bar: u8,
#[pud(group = FooBarBaz)]
baz: u8,
```
Generates:
```rs
FooBarBaz(u8, u8, u8) // and appropriate application
```
Note: Groups do not remove the individual field variants; both coexist.
### Full Example
```rs
#[::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:
```rs
#[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.