Miniconf

crates.io docs QUARTIQ Matrix Chat Continuous Integration

Miniconf enables lightweight (no_std) partial serialization (retrieval) and deserialization (updates, modification) within a tree by key. The tree is backed by structs/arrays/Options 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. Miniconf is completely generic over the serde::Serializer/serde::Deserializer backend and the path hierarchy separator.

Example

```rust use miniconf::{Error, Miniconf, JsonCoreSlash}; use serde::{Deserialize, Serialize};

[derive(Deserialize, Serialize, Copy, Clone, Default)]

enum Either { #[default] Bad, Good, }

[derive(Deserialize, Serialize, Copy, Clone, Default, Miniconf)]

struct Inner { a: i32, b: i32, }

[derive(Miniconf, Default)]

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(2))]
array_miniconf: [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(2))]
option_miniconf: Option<Inner>,
// Hiding elements of an Array of Miniconf items
#[miniconf(defer(3))]
array_option_miniconf: [Option<Inner>; 2],

}

let mut settings = Settings::default(); let mut buf = [0; 64];

// Atomic updates by field name settings.setjson("/foo", b"true")?; asserteq!(settings.foo, true); settings.setjson("/enum", br#""Good""#)?; settings.setjson("/struct", br#"{"a": 3, "b": 3}"#)?; settings.setjson("/array", b"[6, 6]")?; settings.setjson("/option", b"12")?; settings.set_json("/option", b"null")?;

// Deep access by field name in a struct settings.setjson("/structdefer/a", b"4")?; // ... or by index in an array settings.setjson("/arraydefer/0", b"7")?; // ... or by index and then struct field name settings.setjson("/arrayminiconf/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.setjson("/optiondefer", b"13"), Err(Error::Absent(1))); settings.optiondefer = Some(0); settings.setjson("/optiondefer", b"13")?; settings.optionminiconf = Some(Inner::default()).into(); settings.setjson("/optionminiconf/a", b"14")?; settings.arrayoptionminiconf[1] = Some(Inner::default()).into(); settings.setjson("/arrayoption_miniconf/1/a", b"15")?;

// Serializing elements by path let len = settings.getjson("/struct", &mut buf)?; assert_eq!(&buf[..len], br#"{"a":3,"b":3}"#);

// Iterating over all paths for path in Settings::iterpaths::("/") { let path = path.unwrap(); // Serializing each match settings.getjson(&path, &mut buf) { Ok(len) => { // Deserialize again settings.setjson(&path, &buf[..len])?; } // Some settings are still None and thus their paths are expected to be absent Err(Error::Absent()) => {} e => { e?; } } }

Ok::<(), miniconf::Error>(())

```

MQTT

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

Discover the complete unique prefix of an application listening to messages

under the topic quartiq/application/12345 and set its foo setting to true.

python -m miniconf -d quartiq/application/+ foo=true ```

Derive macro

For structs Miniconf offers a [macro@Miniconf] derive macro. The macro implements the [Miniconf] trait that exposes access to serialized field values through their path. All types supported by either [serde] (and the serde::Serializer/serde::Deserializer backend) or Miniconf can be used as fields.

Structs, arrays, and Options can then be cascaded to construct more complex trees. When using the derive macro, the behavior and tree recursion depth can be configured for each struct field using the #[miniconf(defer(Y))] attribute.

See also the [Miniconf] trait documentation for details.

Keys and paths

Lookup into the tree is done using an iterator over [Key] items. usize indices or &str names are supported.

Path iteration is supported with arbitrary separator between names.

Formats

Miniconf is generic over the serde backend/payload format and the path hierarchy separator.

Currently support for / as the path hierarchy separator and JSON (serde_json_core) is implemented through the [JsonCoreSlash] super trait.

Transport

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.

Limitations

Deferred/deep/non-atomic access to inner elements of some types is not yet supported, e.g. enums other than [core::option::Option]. These are still however usable in their atomic serde form as leaves.

Features

The mqtt-client and json-core features are enabled by default.