miniconf turns selected values inside heterogeneous Rust data into a small
runtime-addressable tree. It is no_std by default, uses Serde for leaf
payloads, and lets the same settings type serve human tools, compact embedded
links, generated schemas, and transport protocols.
Use it when a typed Rust configuration or state tree should be:
- accessed one leaf at a time by path or compact key
- exposed over a transport without giving that transport ownership of the data
- discovered by tools through schema iteration, semantics, and metadata
- reused across CLIs, SCPI-like protocols, MQTT, tests, or generated UI/API surfaces
Derive [Tree] for the settings type. Fields whose types also implement the
Tree* traits become internal nodes; ordinary Serde values are leaves.
use miniconf::{json_core, Tree};
#[derive(Default, Tree)]
struct Settings {
enabled: bool,
output: Output,
}
#[derive(Default, Tree)]
struct Output {
gain: [u16; 2],
}
let mut settings = Settings::default();
json_core::set(&mut settings, "/enabled", b"true").unwrap();
json_core::set(&mut settings, "/output/gain/1", b"42").unwrap();
let mut buf = [0; 8];
let len = json_core::get(&settings, "/output/gain/1", &mut buf).unwrap();
assert!(settings.enabled);
assert_eq!(&buf[..len], b"42");Start with [json_core] and slash-separated &str paths for human-facing
tools, tests, and protocol sketches. The lower layers are useful when the
boundary needs something more specific:
- [
TreeSchema] and [Schema::nodes()] discover leaves; [Schema::get()] checks one exact key and returns the reached schema. - [
TreeSerialize] and [TreeDeserialize] serialize or update exactly one selected leaf with any Serde format. - [
TreeAny] gives typed host-side access throughcore::any::Any. - [
PathIter], [ConstPathIter], [JsonPathIter], index slices, and [Packed] are interchangeable key boundaries through [IntoKeys]. - [
postcard] with [Packed] gives compact binary key-value messages. - [
json_schema] builds host/tooling schemas from the same tree. miniconf_mqttis the ready-made MQTT transport.
Tree is a derive shorthand for [macro@TreeSchema], [macro@TreeSerialize],
[macro@TreeDeserialize], and [macro@TreeAny]. Derive attributes live under
#[tree(...)]:
rename = "name"changes a field or variant path segment.skipremoves a field or variant from the tree.flattensplices a single unambiguous child tree into its parent.with = moduledelegates access to a custom implementation module.meta(...)attaches schema metadata when the matching metadata feature is enabled.
Use #[tree(with = leaf)] to keep a type as one Serde leaf even if it also
implements Tree.
use miniconf::{json_core, leaf, Tree};
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize)]
struct Calibration {
offset: i32,
scale: u16,
}
#[derive(Default, Tree)]
struct Settings {
#[tree(rename = "cal", with = leaf)]
calibration: Calibration,
}
let mut settings = Settings::default();
json_core::set(&mut settings, "/cal", br#"{"offset":-3,"scale":10}"#).unwrap();
assert_eq!(settings.calibration.offset, -3);Structs, enums, arrays, tuples, Option<T>, and standard container types can be
combined into larger trees. Option branches and inactive enum variants remain
in the static schema but may return [ValueError::Absent] at runtime.
miniconf is transport agnostic. Any channel that can carry a key and a Serde
payload can use the tree. Keep transport routing, sessions, and buffering in
the transport layer; pass a borrow of the settings tree into miniconf access
functions when a message targets the tree.
Use [Schema::transcode()] to translate one key representation into another.
Use [NodeIter] when publishing, validating, or rendering every leaf; it yields
leaves only and exposes the current indices and schema while walking.
The embedded benchmark compares miniconf against a handwritten serial-style
router for the same settings tree and value codec. Treat the handwritten router
as a routed get/set lower bound, not a feature-equivalent replacement: it omits
schema iteration, metadata, key transcoding, generic key backends, and generated
reflection. The benchmark reports the static schema payload separately so the
routed get/set overhead and reflection data can be judged independently.
- Internal tree enums support unit, newtype, and skipped variants only. Enums with named fields or multi-field tuple variants should stay leaves or use a manual/custom implementation.
- Flattening is accepted only when generated lookup stays structurally unambiguous.
&strkey input is always slash-separated. Use explicit iterator types for other syntaxes or separators.- Schema semantics and metadata are feature-gated reflection data. Do not depend
on them unless
sem,meta-node, ormeta-edgeis enabled as needed.
derive: re-export derive macros fromminiconf_derive; enabled by default.json-core:serde_json_corehelpers for JSON byte slices.json:serde_jsonhelpers.postcard: compact binary helpers usingpostcard.sem,meta-node,meta-edge: retain structured schema semantics, node metadata, and parent-child edge metadata. Constructors and derive output accept these payloads in all builds; without the matching feature, they are discarded and schema accessors returnNoneor empty metadata.trace,schema: serde-reflection tracing and JSON Schema generation.heapless,heapless-09,alloc,std: support for the corresponding storage and platform layers.