# 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(...)]`:
| *(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
| `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