app-json-settings 2.5.0

Tiny typed JSON settings persistence for Rust applications.
Documentation
# Migration to v2

## v2.0.x to 2.1.0

`with_root_dir()` was added as the preferred name for caller-provided storage
roots. Existing `at_custom_dir()` code still works.

Pure UWP support is available through either host-resolved roots or the optional
`uwp` feature.

## 2.1.0 to 2.2.0

`for_app()` was added as the recommended production constructor:

```rust
let manager = ConfigManager::<Settings>::for_app("my-app")?;
```

`try_with_filename()` was added for checked file names:

```rust
let manager = manager.try_with_filename("settings.json")?;
```

`with_filename()` remains available for v2.x compatibility.


## 2.2.0 to 2.3.0

`SaveMode` was added and `save()` now uses `SaveMode::Atomic` by default.

Applications that intentionally want the previous direct overwrite behavior can
select it explicitly:

```rust
let manager = ConfigManager::<Settings>::for_app("my-app")?
    .with_direct_save();
```

The public loading, saving, and update methods remain source-compatible.


## v2.4.x to 2.5.0

`ConfigManager::for_app()` now reports storage-root resolution failure
instead of silently substituting a relative path.

**Who is affected.** Only applications running where the platform
configuration directory cannot be resolved: on Unix (excluding macOS) when
neither `XDG_CONFIG_HOME` nor `HOME` is set, on macOS when `HOME` is not set,
and on Windows when `%APPDATA%` is not set. This essentially never happens on
a normal desktop environment. It can happen for services or containers run
without a user environment — for example, a systemd unit without `User=`, or
some minimal container configurations.

**How it shows up.** Previously, `for_app()` would succeed and silently
resolve to a path under the current working directory. Now it returns
`Err(ConfigError::Platform(_))`, with a message naming the missing
environment variable.

**Before:**

```rust
// Succeeded even without HOME/%APPDATA%, silently writing under $CWD.
let manager = ConfigManager::<Settings>::for_app("my-app")?;
```

**After:**

```rust
let manager = match ConfigManager::<Settings>::for_app("my-app") {
    Ok(manager) => manager,
    Err(ConfigError::Platform(message)) => {
        eprintln!("could not resolve a config directory: {message}");
        // Supply a path explicitly instead of relying on platform resolution.
        ConfigManager::<Settings>::new().with_root_dir(chosen_path)
    }
    Err(error) => return Err(error),
};
```

`ConfigManager::new()` is unaffected — it keeps falling back to the current
directory, since it cannot report an error without breaking its signature.
Code that only ever calls `new()` sees no behavior change from this release.