# toml-edit-derive

[![crates.io](https://img.shields.io/crates/v/toml-edit-derive.svg)](https://crates.io/crates/toml-edit-derive)
[![docs.rs](https://img.shields.io/docsrs/toml-edit-derive)](https://docs.rs/toml-edit-derive)

Typed, ergonomic read/write access to [`toml_edit`](https://docs.rs/toml_edit) documents via a `#[derive(TomlEdit)]` macro **without losing comments or formatting**.

`toml_edit` preserves a document's layout but exposes it through stringly-typed `doc["key"]` indexing. This crate generates typed accessors from your structs
and enums so you get autocomplete and type-checked getters/setters while edits flow back into the original document, comments intact.

## Example

```rust
use toml_edit_derive::TomlEdit;

#[derive(TomlEdit)]
struct Config {
    name: String,
    retries: i64,
}

let mut doc = ConfigTomlEdit::parse("# my app\nname = \"app\"\nretries = 3\n").unwrap();

// typed reads
assert_eq!(doc.name(), Some("app".to_owned()));
assert_eq!(doc.retries(), Some(3));

// typed writes — comment is preserved
doc.retries_mut().set(5);
let out = doc.to_toml_string();
assert!(out.contains("# my app"));
assert!(out.contains("retries = 5"));
```

For each `#[derive(TomlEdit)] struct Foo`, the macro generates:

- `FooTomlEdit` — owns a `DocumentMut`; `parse()` / `to_toml_string()` plus
  `field()` / `field_mut()` accessors (return `Option<T::View<'_>>` / `FieldEntry<T>`).
- `FooTomlEditView<'a>` — borrowed, read-only view (for nested values).
- `FooTomlEditMut<'a>` — borrowed mutable view.

## Safety

- **Integer bounds:** Integer values outside the valid range for the target type return `None` instead of silently wrapping. E.g., `u8_field = 256` → `None`. TOML integers are `i64`; writing a value outside `i64::MIN..=i64::MAX` (e.g. `u64::MAX`) panics — fields that may exceed this range should be stored as strings instead.
- **Enum tag collisions:** Duplicate normalized variant tags in tagged enums are detected at derive time with a clear error message.
- **Type validation:** `#[serde(flatten)]` requires a struct type (not a scalar) — validated at compile time.

## Supported field types

- Primitives: `String`, `bool`, all integer types (`i8`–`i128`, `u8`–`u128`, `usize`, `isize`), `f32`/`f64`. Note: `f32` is stored as TOML `f64` and may lose precision on read-back.
- String-backed scalars via the `TomlEditValue` trait: `PathBuf`, `SocketAddr`
  (and your own types — implement `TomlEditValue`).
- `Option<T>` — absent key maps to `None`; `field_mut().remove()` removes the key.
- Nested `#[derive(TomlEdit)]` structs — as `[table]` or inline `{ .. }`.
- `Vec<T>` — primitive arrays and arrays-of-tables (`[[entries]]`), with full mutable access:
  - `get_mut(i)` → per-element mutable handle (works for both scalar and table arrays).
  - `iter_mut()` → iterate with mutable access.
  - `push_empty()` → append a new table (struct arrays only).
  - `remove(i)` → delete by index.
- `HashMap<String, V>` / `BTreeMap<String, V>` — string-keyed tables with a
  mutable proxy: `get_mut(key)`, `insert_empty(key)`, `iter_mut()`, `remove(key)`.
- Unit enums (serialize to a string value) and tagged struct enums
  (`#[serde(tag = "...")]`).

A subset of `serde` attributes is honored: `rename`, `rename_all`, `skip`, `skip_serializing`, `skip_deserializing`, `tag`, and `flatten` (merges a nested derived struct's fields into the parent table).

## API Patterns

### Reading

Reads return `Option<T::View<'_>>`. Missing keys map to `None`.

```rust
doc.name()           // Option<String>
doc.inner()          // Option<InnerTomlEditView<'_>>
doc.tags()           // Option<Vec<String>>
```

### Writing

Writes use `FieldEntry<T>`, which mirrors `Option<T>` semantics:

```rust
// Set a value (insert if absent, replace if present)
doc.name_mut().set("new name");

// Get-or-insert with a default
doc.name_mut().get_or_insert("default".to_owned());

// Get-or-insert an empty table (for struct fields)
doc.inner_mut().get_or_insert_empty().field_mut().set(5);

// Remove a key
doc.name_mut().remove();

// Check if present
if doc.name_mut().is_some() { ... }
```

### Array and Map Mutation

For `Vec<T>` and `HashMap<String, T>`, use the mutable proxy:

```rust
// Scalar array
let mut arr = doc.tags_mut().get_mut().unwrap();
arr.get_mut(0).unwrap().set("new tag");
for mut tag in arr.iter_mut() {
    tag.set(tag.get().unwrap().to_uppercase());
}

// Table array (array-of-tables)
let mut entries = doc.entries_mut().get_mut().unwrap();
entries.push_empty().id_mut().set(999);

// String-keyed table
let mut settings = doc.settings_mut().get_mut().unwrap();
settings.insert_empty("new_key").value_mut().set(42);
```

## License

Licensed under either of [Apache-2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT) at your option.
