Miniconf enables lightweight (no_std
) partial serialization (retrieval) and deserialization
(updates, modification) within a hierarchical namespace by path. The namespace is backed by
structs and arrays of serializable types.
Miniconf can be used as a very simple and flexible backend for run-time settings management in embedded devices over any transport. It was originally designed to work with JSON (serdejsoncore) payloads over MQTT (minimq) and provides a comlete MQTT settings management client and a Python reference implementation to ineract with it.
```rust use miniconf::{Error, Miniconf}; use serde::{Deserialize, Serialize};
enum Either { #[default] Bad, Good, }
struct Inner { a: i32, b: i32, }
struct Settings {
// Atomic updtes by field name
foo: bool,
enum: Either,
struct: Inner,
array: [i32; 2],
option: Option
// Exposing elements of containers
// ... by field name
#[miniconf(defer)]
struct_defer: Inner,
// ... or by index
#[miniconf(defer)]
array_defer: [i32; 2],
// ... or deferring to two levels (index and then inner field name)
#[miniconf(defer)]
array_miniconf: miniconf::Array<Inner, 2>,
// Hiding paths by setting the Option to `None` at runtime
#[miniconf(defer)]
option_defer: Option<i32>,
// Hiding a path and deferring to the inner
#[miniconf(defer)]
option_miniconf: miniconf::Option<Inner>,
// Hiding elements of an Array of Miniconf items
#[miniconf(defer)]
array_option_miniconf: miniconf::Array<miniconf::Option<Inner>, 2>,
}
let mut settings = Settings::default(); let mut buf = [0; 64];
// Atomic updates by field name settings.set("foo", b"true")?; asserteq!(settings.foo, true); settings.set("enum", br#""Good""#)?; settings.set("struct_", br#"{"a": 3, "b": 3}"#)?; settings.set("array", b"[6, 6]")?; settings.set("option", b"12")?; settings.set("option", b"null")?;
// Deep access by field name in a struct settings.set("structdefer/a", b"4")?; // ... or by index in an array settings.set("arraydefer/0", b"7")?; // ... or by index and then struct field name settings.set("array_miniconf/1/b", b"11")?;
// If a deferred Option is None
it is hidden at runtime and can't be accessed
settings.optiondefer = None;
asserteq!(settings.set("optiondefer", b"13"), Err(Error::PathAbsent));
settings.optiondefer = Some(0);
settings.set("optiondefer", b"13")?;
settings.optionminiconf = Some(Inner::default()).into();
settings.set("optionminiconf/a", b"14")?;
settings.arrayoptionminiconf[1] = Some(Inner::default()).into();
settings.set("arrayoption_miniconf/1/a", b"15")?;
// Serializing elements by path let len = settings.get("struct", &mut buf)?; asserteq!(&buf[..len], br#"{"a":3,"b":3}"#);
// Iterating over and serializing all paths for path in Settings::iter_paths::<3, 32>().unwrap() { let ret = settings.get(&path, &mut buf);
// Some settings are still `None` and thus their paths are expected to be absent
assert!(matches!(ret, Ok(_) | Err(Error::PathAbsent)));
}
```
There is an MQTT-based client that implements settings management over the MQTT protocol with JSON payloads. A Python reference library is provided that interfaces with it.
```sh
quartiq/application/12345
and set its foo
setting to true
.python -m miniconf -d quartiq/application/+ foo=true ```
For structs with named fields, Miniconf offers a derive macro to automatically assign a unique path to each item in the namespace of the struct. The macro implements the [Miniconf] trait that exposes access to serialized field values through their path. All types supported by [serdejsoncore] can be used as fields.
Elements of homogeneous [core::array]s are similarly accessed through their numeric indices.
Structs, arrays, and Options can then be cascaded to construct a multi-level namespace.
Namespace depth and access to individual elements instead of the atomic updates
is configured at compile (derive) time using the #[miniconf(defer)]
attribute.
Option
is used with #[miniconf(defer)]
to support paths that may be absent (masked) at
runtime.
While the [Miniconf] implementations for [core::array] and [core::option::Option] by provide atomic access to their respective inner element(s), [Array] and [Option] have alternative [Miniconf] implementations that expose deep access into the inner element(s) through their respective inner [Miniconf] implementations.
The path hierarchy separator is the slash /
.
Values are serialized into and deserialized from JSON.
Miniconf is designed to be protocol-agnostic. Any means that can receive key-value input from some external source can be used to modify values by path.
Deferred (non-atomic) access to inner elements of some types is not yet supported. This includes: * Complex enums (other than [core::option::Option]) * Tuple structs (other than [Option], [Array])
mqtt-client
Enabled the MQTT client feature. See the example in [MqttClient].