Skip to main content

Crate miniconf

Crate miniconf 

Source
Expand description

§miniconf

crates.io docs QUARTIQ Matrix Chat Continuous Integration

miniconf makes typed Rust data addressable: read a value, change it, discover what else is there. Derive one tree and reuse it in a shell, a snapshot, an inspector, or a protocol.

The core is no_std and needs no allocator. Serde encodes the values; Miniconf selects them by path.

§Quick Start

Create a host executable (no hardware or network required):

cargo new miniconf-demo
cd miniconf-demo
cargo add miniconf@0.21.1

Replace src/main.rs with the following and run cargo run. Derive Tree to expose the fields of each settings struct.

use miniconf::{json_core, ConstPath, Tree, TreeSchema};

#[derive(Default, Tree)]
struct Settings {
    enabled: bool,
    output: Output,
}

#[derive(Default, Tree)]
struct Output {
    gain: [u16; 2],
}

fn main() {
    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");

    const DEPTH: usize = Settings::SCHEMA.max_depth();
    for path in Settings::SCHEMA.nodes::<ConstPath<String, '/'>, DEPTH>() {
        println!("{}", path.unwrap());
    }
}

This prints /enabled, /output/gain/0, and /output/gain/1. Try adding a field: it gets a path without another dispatch table. The final loop discovers those paths from the type’s schema. Here it uses the host’s String; a fixed-capacity string also works when no allocator is available.

§Tree Shape

Nested trees expose their fields separately, as Output does above. A leaf is read or written as one Serde value. On the gain field, #[tree(with = miniconf::leaf)] would make the array one leaf: /output/gain would read or write [0, 42] as a whole instead of exposing each element. #[tree(rename = "level")] would change its path segment to level.

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.

§Control Changes

Standard leaf updates through JSON-core and Postcard decode and finalize before assignment. Raw Serde sources deserialize in place and can leave partial changes on failure. Custom setters control their own side effects. Stage multi-leaf updates and commit after all calls succeed. Hardware updates and persistence remain application decisions.

Use #[tree(with = module)] to enforce rules for a field. The integration fixture (examples/common.rs) shows read-only fields and DAC range checking with a scratch copy. Metadata such as max = "4095" describes the range; the custom deserializer enforces it.

§Reuse The Tree

Stabilizer’s miniconf-settings uses one tree for a shell and snapshots: get and set address live values, while snapshots walk the leaves to save and restore them. Add a field and both consumers can reach it. The application handles USB framing, hardware updates, and flash storage. This upper-layer crate is currently unpublished.

The checkout examples explore other consumers using the same integration fixture. Its paths and values are also used by tests and the embedded benchmark; use the small quickstart tree above for experiments.

ExampleRunWhat it adds
examples/cli.rscargo run --example cli -- --output-dac-1 2048Command-line options
examples/packed.rscargo run --example packed --features postcardA binary leaf round trip in fixed buffers
examples/trace.rscargo run --example trace --features schemaHost-side JSON and JSON Schema
examples/scpi.rscargo run --example scpiCustom command syntax, not a complete SCPI implementation

§Build A Consumer

Tree derives four independent capabilities: TreeSchema describes the leaves, TreeSerialize reads them, TreeDeserialize writes them, and TreeAny borrows their values through core::any::Any. An inspector can require only the first two. The caller supplies framing, buffers, and scheduling.

Paths are one way to select a leaf. Index slices and Packed keys use the same IntoKeys interface; Schema::transcode() converts between representations. Compact keys belong to a particular schema, so resolve them again when the tree changes. Schema::nodes() discovers leaves and Schema::get() looks up one key.

Choose the leaf codec independently: json_core uses JSON byte slices, postcard uses compact binary payloads. miniconf_mqtt and miniconf_coap are ready-made protocol consumers.

§Code Size

The embedded benchmark in tests/benchmark compares the same get/set workload and codec against handwritten dispatch. It reports program size, schema bytes, and observed stack use. The manual handler omits discovery and reflection; the results are workload-specific, not a worst-case stack bound. Run it with your tree when size matters.

§Limits

  • 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.
  • &str key 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, or meta-edge is enabled as needed.

§Features

  • derive: re-export derive macros from miniconf_derive; enabled by default.
  • json-core: serde_json_core helpers for JSON byte slices.
  • json: serde_json helpers.
  • postcard: compact binary helpers using postcard.
  • 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 return None or empty metadata.
  • trace, schema: serde-reflection tracing and JSON Schema generation.
  • heapless, heapless-09, alloc, std: support for the corresponding storage and platform layers.

§Stability

miniconf follows Cargo’s SemVer compatibility guidelines, including Rust’s policy for trait implementations. For 0.y.z releases, breaking changes bump y; compatible changes bump z.

Modules§

compact_schema
Compact paged schema serialization.
deny
Deny access tools.
json
Utilities using serde_json
json_core
TreeSerialize/TreeDeserialize with “JSON and /”.
json_schema
JSON Schema tools.
leaf
Leaf implementation using serde::{Serialize, Deserialize}
passthrough
Passthrough Tree*
postcard
Read and write leaf values with the Postcard binary format.
str_leaf
TryFrom<&str>/AsRef<str> leaf
trace
Schema tracing

Structs§

Chain
Concatenate two normalized key cursors.
ConstPath
Const-specialized output path with named selector segments separated by a const separator.
ConstPathIter
Const-specialized path iterator for boundary key input.
ExactSize
Counting wrapper for iterators with known exact size
Homogeneous
A representative schema item for a homogeneous array
Indices
Sequence of child indices identifying a node in a TreeSchema.
InternalSchema
Static schema payload for an internal node.
JsonPath
JSON-style path notation output.
JsonPathIter
JSON-style path iterator for boundary key input.
KeysIter
Explicit normalized wrapper for iterator-shaped key inputs.
Leaf
Serialize/Deserialize/Any leaf
Lookup
Result of an exact key lookup.
Meta
Immutable schema metadata.
Named
A named schema item
NodeIter
Node iterator
NodeSchema
Shared static schema payload for one tree node.
Numbered
A numbered schema item
Packed
A bit-packed normalized key cursor over multiple child indices.
Path
Output path with named selector segments separated by a separator char.
PathIter
Runtime-separated path iterator for boundary key input.
ResolveError
Error returned by Schema::resolve_into().
Sem
Structured machine-readable schema semantics.
Shape
Metadata about a crate::TreeSchema namespace.

Enums§

DescendError
Errors that can occur while visiting nodes with crate::Schema::descend().
Internal
An internal node with children
KeyError
Errors that can occur when using the Tree traits.
Schema
Static schema for one tree node.
SerdeError
Compound errors
Ty
Compact semantic leaf type.
ValueError
Errors that can occur while accessing a value.

Constants§

MAYBE_ABSENT_SEM
Structured semantics for a node that may be absent at runtime.
ONEOF_SEM
Structured semantics for a mutually exclusive named node.

Traits§

IntoKeys
Boundary input that can be normalized into a Keys cursor.
Key
Convert one selector segment into a child index for an internal node schema.
Keys
Normalized cursor over selector segments.
Transcode
Look up a key path in a Schema and transcode it.
TreeAny
Access any node by a normalized key cursor.
TreeDeserialize
Deserialize a leaf node by a normalized key cursor.
TreeDeserializeOwned
Shorthand for owned deserialization through TreeDeserialize.
TreeDeserializer
A source for deserializing tree leaves.
TreeSchema
Traversal and iteration of key paths in a tree.
TreeSerialize
Serialize a leaf node by a normalized key cursor.

Derive Macros§

Tree
Derive the TreeSchema, TreeSerialize, TreeDeserialize, and TreeAny traits for a struct or enum.
TreeAny
Derive the TreeAny trait for a struct or enum.
TreeDeserialize
Derive the TreeDeserialize trait for a struct or enum.
TreeSchema
Derive the TreeSchema trait for a struct or enum.
TreeSerialize
Derive the TreeSerialize trait for a struct or enum.