rtb-config 0.6.5

Layered, typed configuration backed by figment. Part of the phpboyscout Rust toolkit.
Documentation
---
title: Cargo features and dependencies
description: The two optional features rtb-config ships, what each one adds to the API and to your dependency graph, and the crate's MSRV and always-on dependencies.
---

# Cargo features and dependencies

`rtb-config` has two Cargo features and **both are off by default**. Everything on
the [API reference](api.md) that is not marked with a feature works with a plain
dependency line.

```toml
[dependencies]
rtb-config = "0.6"
```

## Which feature do I need?

| Feature | Turns on | Pulls in | Default |
|---|---|---|---|
| `hot-reload` | `Config::watch_files`, the `WatchHandle` type, the `rtb_config::watch` module | `notify`, `notify-debouncer-full` | off |
| `mutable` | `Config::schema`, `Config::write` | `schemars`, `serde_json`, `serde-saphyr`'s `serialize` feature, `toml` | off |

```toml
# a long-running service that reloads on file change
rtb-config = { version = "0.6", features = ["hot-reload"] }

# a CLI with `config set` / `config schema` subcommands
rtb-config = { version = "0.6", features = ["mutable"] }

# both
rtb-config = { version = "0.6", features = ["hot-reload", "mutable"] }
```

The features are independent. Neither implies the other, and every combination is
supported.

## What if I call a feature-gated method without the feature?

It does not exist, so the build fails at compile time with `no method named
watch_files found` or `no function or associated item named schema found`. There is
no runtime fallback and nothing silently degrades.

If a method you expected is missing and the feature *is* enabled, check the trait
bounds: `write` and `schema` also need `C: Serialize + JsonSchema`. See
[which trait bounds does my config struct need](api.md#which-trait-bounds-does-my-config-struct-need).

## Why is hot-reload not on by default?

A file watcher costs an OS watch handle, a background thread and two more crates, and
most consumers of a config crate are short-lived CLI invocations that read config once
and exit. Making the watcher opt-in keeps that common case cheap. The same reasoning
applies to `mutable`: a tool that never writes config back should not carry a YAML and
a TOML serialiser to do it.

## What does rtb-config depend on unconditionally?

| Crate | Why |
|---|---|
| `figment` | The layering and merge engine, with its `toml`, `json` and `env` features |
| `serde-saphyr` | Parses embedded defaults and user files as YAML, through a figment `Format` of the crate's own |
| `serde` | The `Deserialize` derive your config struct uses |
| `arc-swap` | The atomic pointer swap behind `get` and `reload` |
| `tokio` | `tokio::sync::watch` powers `Config::subscribe` |
| `thiserror` | The `ConfigError` derive |
| `miette` | Diagnostic codes and help text on every error variant |

Two of those are worth calling out because they are larger than they need to be for
what the crate uses:

- **`tokio` is not optional and is declared with `features = ["full"]`**, even though
  only `tokio::sync::watch` is used. If you are counting build time or binary size,
  that is where it goes.
- **`figment` is built with the `toml` and `json` features**, but only its
  environment provider is wired up; YAML comes from `serde-saphyr`, not figment's
  `yaml` feature, which would pull in the unmaintained `serde_yaml`. `ConfigBuilder::user_file` always parses YAML
  (see [can I use a TOML or JSON config file]../explanation/limitations.md#can-i-use-a-toml-or-json-config-file).

## Do I need a tokio runtime?

Only to await changes. `build`, `get`, `reload` and `write` are ordinary synchronous
calls and work in a program with no async runtime at all. `Config::subscribe` returns
a `tokio::sync::watch::Receiver`, and `rx.borrow()` is synchronous too. Only
`rx.changed().await` needs an async context.

`watch_files` runs on a plain OS thread, not a tokio task, so the watcher itself does
not need a runtime either.

## What Rust version does this need?

| Field | Value |
|---|---|
| Edition | 2021 |
| MSRV (`rust-version`) | 1.89, set by `serde-saphyr` 1.3 |
| Toolchain used in CI | pinned exactly in `rust-toolchain.toml` |

CI pins one exact stable rather than tracking the floating `stable` channel, so a new
rustc release cannot change the build without a reviewed toolchain bump.

The crate is `#![forbid(unsafe_code)]`, not `deny`, which a module could locally
override. There is no `unsafe` block anywhere in the shipped code.

## Next

- [API reference]api.md: the full surface, feature by feature
- [Reload configuration at runtime]../how-to/reload-configuration-at-runtime.md, using `hot-reload`
- [Write config back and export its schema]../how-to/write-config-and-export-a-schema.md, using `mutable`