defaulted 0.1.3

Trait and derive macro for testing whether a value equals its default state, with per-field customization and optional serde integration
Documentation
# defaulted

Trait and derive macro for testing whether a value equals its default state,
with per-field customization and optional serde integration.

## Overview

Rust's standard [`Default`] trait constructs default values, but it says nothing
about whether an *existing* value is still at its default state.
This crate fills that gap with a single-method trait:

```rust
pub trait Defaulted {
    fn is_defaulted(&self) -> bool;
}
```

The crate also provides:

- Built-in impls for all primitive types, `String`, `Option`, `Vec`,
  `HashMap`, `HashSet`, etc.
- A derive macro `#[derive(Defaulted)]` (behind `derive` feature) to generate the impl automatically for a user-defined struct or enum.
- An attribute macro `#[skip_serializing_defaults]` (behind `derive` and `serde` features)
  making serde skip serialization for fields that are in their default state.

## Usage

Add the crate to `Cargo.toml`:

```toml
[dependencies]
defaulted = { version = "0.1", features = ["derive"] }
```

Enable serde support with:

```toml
[dependencies]
defaulted = { version = "0.1", features = ["derive", "serde"] }
```

### Basic derive

```rust
use defaulted::Defaulted;

#[derive(Default, Defaulted)]
struct Config {
    width: u32,
    height: u32,
    label: String,
}

let c = Config::default();
assert!(c.is_defaulted());

let c2 = Config { width: 1920, ..Config::default() };
assert!(!c2.is_defaulted());
```

### Per-field customization

Each field can opt into one of several checker strategies via
`#[defaulted(...)]`:

| Attribute | Meaning |
|---|---|
| *(none)* | Delegate to the field type's `Defaulted` impl |
| `#[defaulted(default = expr)]` | Field is "default" when it equals `expr` |
| `#[defaulted(with = func)]` | Call `func(&field)` -- true means "default" |
| `#[defaulted(with = \|v\| ...)]` | Inline closure |
| `#[defaulted(ignore)]` | Always treat this field as default (skip it) |

```rust
use defaulted::Defaulted;

#[derive(Defaulted)]
struct Request {
    path: String,

    // "default" timeout is 30, not 0
    #[defaulted(default = 30)]
    timeout_secs: u64,

    // treat any non-empty tag list as non-default
    #[defaulted(with = Vec::is_empty)]
    tags: Vec<String>,

    // internal counter -- never affects defaulted
    #[defaulted(ignore)]
    _request_id: u64,
}
```

### Enums

Mark exactly one variant with `#[defaulted(default)]`:

```rust
use defaulted::Defaulted;

#[derive(Defaulted)]
enum Status {
    #[defaulted(default)]
    Idle,
    Running { task: String },
    Failed(u32),
}

assert!(Status::Idle.is_defaulted());
assert!(!Status::Running { task: "build".into() }.is_defaulted());
```

Enum variants with fields follow the same per-field rules as structs.

### Generating `Default` from field defaults

`#[defaulted(Default)]` on a struct generates a `Default` impl whose
field values match the `#[defaulted(default = expr)]` annotations, so the two
stay in sync automatically:

```rust
use defaulted::Defaulted;

#[derive(Debug, PartialEq, Defaulted)]
#[defaulted(Default)]
struct Settings<'a> {
    #[defaulted(default = "dark")]
    theme: &'a str,

    #[defaulted(default = 8080)]
    port: u16,

    enabled: bool,
}

let s = Settings::default();
assert_eq!(s.theme, "dark");
assert_eq!(s.port, 8080);
assert!(s.is_defaulted());
```

### Serde integration

`#[skip_serializing_defaults]` rewrites a struct's fields to add
`#[serde(skip_serializing_if = "...")]` (and `#[serde(default = "...")]` for
`EqualsValue` fields) automatically, so default-valued fields are omitted from
serialized output and restored correctly on deserialization.

```rust
use defaulted::Defaulted;
use serde::{Deserialize, Serialize};

#[defaulted::skip_serializing_defaults] // must go before #[derive(Serialize)]
#[derive(Debug, Serialize, Deserialize, Defaulted)]
#[defaulted(Default)]
struct ApiConfig {
    #[defaulted(default = "https://api.example.com")]
    base_url: String,

    #[defaulted(default = 60)]
    timeout_secs: u32,

    tags: Vec<String>,
}

let c = ApiConfig::default();
let json = serde_json::to_string(&c).unwrap();
assert_eq!(json, "{}"); // all fields at default -- nothing serialized
```

## Feature flags

| Flag | Default | Description |
|---|---|---|
| `std` | yes | Enables impls for `HashMap`, `Mutex`, `Path`, etc. Implies `alloc`. |
| `alloc` | yes (via `std`) | Enables impls for `String`, `Vec`, `Box`, `Arc`, `Cow`, etc. |
| `derive` | no | Enables `#[derive(Defaulted)]` |
| `serde` | no | Enables `#[skip_serializing_defaults]` attribute macro |
| `serde-json` | no | Enables impls for `serde_json::Value` and `serde_json::Map` |
| `serde-yaml` | no | Enables impls for `serde_yaml::Value` and `serde_yaml::Mapping` |
| `bytes` | no | Enables impls for `Bytes` and `BytesMut` |
| `indexmap` | no | Enables impls for `IndexMap` and `IndexSet` |
| `smallvec` | no | Enables impls for `SmallVec` |

## `no_std` support

Disable default features and optionally enable `alloc`:

```toml
[dependencies]
defaulted = { version = "0.1", default-features = false }              # core only
defaulted = { version = "0.1", default-features = false, features = ["alloc"] }  # core + alloc
```

## License

MIT