# 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)
    ),
    phantom_attrs(allow(dead_code)),
    group_attrs(
        FooBarBaz(allow(non_camel_case_types))
    ),
    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                                             |
| `phantom_attrs(...)`        | Attributes applied to the hidden `__` generic variant                                    |
| `group_attrs(...)`          | Attributes applied to generated group variants                                           |
| `whole` / `whole = <Ident>` | Generates a variant holding a full instance of the struct, replacing all fields on apply |

##### `phantom_attrs(...)`

Applies attributes to the hidden `__` variant that is generated when the Pud enum has type generics.

##### `whole` / `whole = Ident`

Generates a variant that holds a full instance of the struct and replaces all fields atomically on apply.

```rs
#[pud(whole)]
struct Foo { a: u8, b: u16 }
```

Generates:

```rs
enum FooPud {
    A(u8),
    B(u16),
    Whole(Foo),  // replaces both fields at once
}
```

```rs
// Usage
foo.apply(FooPud::Whole(Foo { a: 1, b: 2 }));
```

The variant name defaults to `Whole` and can be customized:

```rs
#[pud(whole = Replace)]
struct Bar { x: String, y: bool }
// generates BarPud::Replace(Bar)
```

##### `group_attrs(NAME(...), OTHER(...))`

Applies attributes to generated group variants by group name.

```rs
#[pud(
    group_attrs(
        FooBarBaz(cfg(feature = "grouped"))
    )
)]
```

#### 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.

##### `attrs(...)`

Applies attributes to the generated Pud variant.
_Note_: `cfg`/`cfg_attr` attributes found there will also be applied to the match arm for that field.

```rs
#[pud(attrs(cfg(feature = "unstable")))]
foo: u8,
```

##### `rename = Ident`

Renames the generated Pud variant.

```rs
#[pud(rename = FOO)]
foo: u8,
```

Generates

```rs
FooPud::FOO(u8) // instead of FooPud::foo(u8)
```

##### `skip`

Excludes a field from its generated solo Pud variant. Group variants still include it.

Cannot be combined with `rename` or `attrs`, since those only affect the skipped solo variant.

```rs
#[pud(skip)]
secret: String,
```

##### `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)
    ),
    group_attrs(
        FooBarBaz(cfg(feature = "grouped"))
    ),
)]
#[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.
