dotcfg 0.1.0

Flexible config management for Rust CLIs — your path, your format
Documentation
# dotcfg

Flexible config management for Rust CLIs — your path, your format.

## Why dotcfg?

Most config crates either lock you into a fixed directory strategy or only handle reading. `dotcfg` gives you full control:

- Choose `~/.toolname/` or `~/.config/toolname/` — your call
- TOML or JSON — feature-gated, include only what you need
- Load the whole config or get/set individual keys without touching the rest
- Returns `None` if config doesn't exist — no magic, no forced defaults unless you want them

## Installation

```toml
# TOML only (default)
dotcfg = "0.1"

# JSON only
dotcfg = { version = "0.1", default-features = false, features = ["json"] }

# Both
dotcfg = { version = "0.1", features = ["json"] }
```

## Quick Start

```rust
use dotcfg::DotCfg;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Default)]
struct Config {
    username: String,
    port: u16,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ~/.mytool/config.toml (default)
    let cfg = DotCfg::new("mytool");

    // Save — creates dir if needed
    cfg.save(&Config { username: "tayo".into(), port: 8080 })?;

    // Load — None if file doesn't exist
    let config: Option<Config> = cfg.load()?;

    // Per-key (no need to load whole struct)
    cfg.set("username", "jane")?;
    let val = cfg.get("user.username")?;

    Ok(())
}
```

## Directory Strategy

```rust
// ~/.mytool/config.toml  (default — like .cargo, .ssh)
let cfg = DotCfg::new("mytool");

// ~/.config/mytool/config.toml  (XDG standard, cross-platform)
let cfg = DotCfg::new("mytool").xdg();

// explicit dot (same as default)
let cfg = DotCfg::new("mytool").dot();
```

## Format

```rust
// TOML (default)
let cfg = DotCfg::new("mytool");

// JSON (needs `json` feature)
let cfg = DotCfg::new("mytool").json();

// Custom filename
let cfg = DotCfg::new("mytool").json().filename("settings");
// → ~/.mytool/settings.json  (or ~/.config/... with .xdg())
```

## Loading

```rust
// Returns None if file doesn't exist — you decide what to do
let config: Option<Config> = cfg.load()?;

// Returns an error if file doesn't exist — useful for CLIs that require setup
let config: Config = cfg.load_or_error()?;

// Creates the file with Default values if it doesn't exist — opt-in
let config: Config = cfg.load_or_default()?;
```

## Per-key Get & Set

Get or set individual keys without loading or overwriting the whole config.

Supports flat and nested (one level: `section.field`) keys:

```rust
// Flat
cfg.get("username")?;
cfg.set("username", "tayo")?;

// Nested → [user] table in TOML / nested object in JSON
cfg.get("user.username")?;
cfg.set("user.username", "tayo")?;
```

`set` creates the file/dir if missing and preserves all other keys. Values are stored as strings.

## Other Utilities

```rust
cfg.exists()?;      // does config file exist?
cfg.dir()?;         // config directory path
cfg.file_path()?;   // full file path
cfg.delete_file()?; // delete file, keep dir
cfg.delete_dir()?;  // delete entire dir
```

## Features

| Feature | Default | Description |
|---------|---------|-------------|
| `toml` | ✅ | TOML format support |
| `json` | ❌ | JSON format support |

## License

Dual-licensed under `MIT OR Apache-2.0`. See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE).