tomldir 0.1.7

Lean TOML loader with runtime dot-path access and flattening into maps, optimized for tools and dynamic configs
Documentation
# tomldir

[![Crates.io](https://img.shields.io/crates/v/tomldir.svg)](https://crates.io/crates/tomldir)
[![Docs](https://docs.rs/tomldir/badge.svg)](https://docs.rs/tomldir)
[![CI](https://github.com/abhishekshree/tomldir/actions/workflows/ci.yml/badge.svg)](https://github.com/abhishekshree/tomldir/actions/workflows/ci.yml)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**tomldir** is a lean crate for loading TOML configs into map-based structures with fast dot-path access, deterministic flattening, and easy composition - ideal for CLIs and runtime-driven configuration.

## Features

- **Runtime-friendly:** Load and query config via string key paths like `"server.port"`.
- **Flattened output:** Nested tables become dot-separated keys for easy iteration or overlay with env/flags.
- **Map-first storage:** Uses a `HashMap` by default, but easily swap in `IndexMap` or `BTreeMap`.
- **Thread-safe:** Designed for use with `Arc` and concurrent access.
- **Flexible flattening:** Flatten to any supported map or container with `flatten_into()`.

If you’re coming from Go, tomldir offers a [Viper](https://github.com/spf13/viper)-like workflow: load TOML into a map, access values via dot paths, and flatten configs for runtime use.

## Why tomldir?

Use tomldir when you want:

- Runtime access via string paths like `database.host`
- Easy flattening for CLI flags or environment overlays
- Flexible configs without rigid structs

tomldir intentionally prioritizes simplicity over strong typing.


## Usage

Add to `Cargo.toml`:

```toml
[dependencies]
tomldir = "0.1"
# Optional: for order-preserving keys
indexmap = "2.0"
```

### Basic: Setting up a runtime config

```rust
use tomldir::Config;

fn main() -> tomldir::Result<()> {
    let toml_data = r#"
        [database]
        host = "localhost"
        port = 5432
    "#;

    // Load into default HashMap
    let cfg = Config::from_toml(toml_data)?;

    
    // Thread-safe access (explicit cheap clone)
    let cfg = cfg.shared(); 

    assert_eq!(cfg.get_string("database.host"), Some("localhost"));

    // Or even convert config into env-style keys
    for (k, v) in cfg.flatten() {
        println!("APP_{}={}", k.to_uppercase().replace('.', "_"), v);
    }

    Ok(())
}
```

### Advanced: Preserving Order with `IndexMap`

If you need to iterate over keys in the order they were defined (e.g., for help text generation or consistent output), use `IndexMap`.

```rust
use tomldir::{Config, Value};
use indexmap::IndexMap;

fn main() -> tomldir::Result<()> {
    let toml_data = r#"
        first = 1
        second = 2
    "#;

    // Specify storage type explicitly
    let cfg = Config::<IndexMap<String, Value>>::from_toml_with(toml_data)?;

    let keys: Vec<_> = cfg.flatten_into::<Vec<(String, String)>>()
        .into_iter()
        .map(|(k, _)| k)
        .collect();
    assert_eq!(keys, vec!["first", "second"]);
    
    Ok(())
}
```

You can get an iterator over all flattened key-value pairs as strings:

```rust
for (key, value) in cfg.flatten() {
    println!("{key} = {value}");
}
```

Or collect them into a specific map (like `HashMap<String, String>`) using `flatten_into()`:

```rust
let flat: HashMap<String, String> = cfg.flatten_into();
```

Or flatten into any custom type using `flatten_into()`:

```rust
use std::collections::BTreeMap;

// Flatten to BTreeMap (sorted keys)
let flat_sorted: BTreeMap<String, String> = cfg.flatten_into();

// Flatten to Vec
let flat_vec: Vec<(String, String)> = cfg.flatten_into();
```

## Comparison

| Feature | serde + toml | config | tomldir |
|----------|-------------|---------|-----------|
| Strong typing || ⚠️ ||
| Dot-path runtime access || ⚠️ ||
| Flattening support || ⚠️ ||
| Minimal boilerplate || ⚠️ ||

## License

MIT