Limitations & Not Planned
Several refusals below are stated against Go's Viper, because that is where the behaviour being refused is best known. What this project took from it is in CREDITS.md.
Limitations
- Every top-level key in a config file must be a table, with one exception:
$schema, so a JSON file can point at the schema that describes it. Sections are figment profiles and a profile has to be a map, so a stray"_comment": "..."at the top level is an error — one that now names the key and says why. - TOML datetimes are not modelled and deserialize as a table.
- The macro refers to the crate as
::dynamic_config, so renaming the dependency is not supported. - Error messages name the environment prefix rather than the exact variable, because that is the granularity figment reports.
Not planned
Each of these is a real request with a real answer. They are refused rather than unbuilt, so that nobody spends an afternoon discovering the reason — and each says what would reopen it.
A ReloadExecutor / ReloadPolicy abstraction
Exists under another name: set_blocking_executor already steers where the
blocking half of a reload runs, and the tokio feature installs the blocking
pool. A second abstraction over the same choice would be a synonym. Reopened
by a steering need the executor hook cannot express.
on_reload_async
changes() is the async reload event — a Future on your executor, free
to await whatever the reaction needs. See
The Reload Lifecycle. Reopened by nothing; this is
what changes() is for.
Splitting the core into -core/-watch/-schema crates
Feature flags already give the isolation a split would: disable a feature and the dependency is gone. A crate split adds a version matrix to maintain without removing anything anyone is forced to take. Reopened by a feature whose dependency cannot be made optional.
Fewer official store backends
The eight exist, are tested against real servers, and are Beta as of 0.6.1. Withdrawing shipped crates punishes their users to save unshipped maintenance. Revisited per crate if one's client dependency becomes unmaintainable.
Nested profiles from figment
figment's profiles are a general mechanism. This crate spends them on
sections — builder("db") selects the db profile — and re-implements the
profile idea on top with profile_env and sibling files
(config.production.toml). So a provider handed to
Source::provider cannot carry its own
profiles through.
The difficulty is not any one part; it is that select(key), the section
mapping, profile_env, sibling files, check(), source_of and every
diagnostic that names a section all assume the current arrangement. Changing it
means giving sections a different mechanism and rewriting the layering
underneath everything that reads well today.
What would reopen it: a figment provider whose own profiles you need, where
Source::provider plus profile_env genuinely cannot express what you are
after.
A swappable loader backend
"Make figment a plug-in" reads like extracting a Loader trait. It is not,
and the reason is worth writing down once rather than re-deriving.
figment reaches nineteen modules here, and not as a parser. Snapshot holds
figment's tree. Sections are figment profiles. Provenance is figment
metadata, converted at the one moment the figment that knew is still alive —
which is what explain, check and source_of read to say which layer
answered. Every layer, .env and the environment included, is a figment
provider in one table. A trait extracted across that seam would either carry
Metadata and Profile in its own signature — leaking exactly what the
figment feature
exists to keep optional — or drop provenance, which is most of what this crate
sells.
The two things people actually want from it already exist and are narrower:
- A format this crate does not read is
Source::provider: three methods, and it has carried the section and provenance contracts since 0.2. - Documents to combine before the loader sees them — a store crate reading
a prefix, a tool folding a fragment directory into one file — is
Value::parse/merge/overlapping_paths/render, described in Writing a Store. It hands over the parsing this crate already compiles, so nothing outside has to takeserde_json,tomlandserde_yamlas direct dependencies to re-do it.
What would reopen it: a backend that resolves layered providers, profile selection and loose environment typing and carries provenance — at which point the argument is about which backend, not about whether there is a seam.
Case-insensitive keys
Viper lowercases everything. It hides typos — Prot and port become the same
key, so unknown-key detection can never
tell you about the first — and it cannot round-trip: a configuration read and
written back comes out in different case from the one a person wrote.
What would reopen it: nothing. This one is a principle rather than a cost.
HCL, Java properties, INI
Each is a parser and a set of edge cases for a format nobody here has asked for, and none of them is something figment provides.
The answer that is not a fork:
Source::provider
takes any figment provider, so a crate that parses one of these wires in without
this one growing a dependency.
Independent instances
Example Go Viper needs them because its default instance is a global. Here every configuration type already has its own storage, keyed by the type — the same isolation without the bookkeeping.
Inferring a type from a default value
serde already knows the type. Viper's SetTypeByDefaultValue exists because
Go's map[string]interface{} does not.
A service-account JSON key for Firestore
Signing one means an RS256 stack inside a configuration library, and Google's own guidance is that a downloaded key is the option of last resort. Workload identity covers GKE, Cloud Run, GCE and Cloud Functions; anything else can mint a token outside the process and pass it in.
Roadmap
ROADMAP.md is what might still be built, and why each item is not obvious. It is short on purpose.