Skip to content

Commit

Permalink
Merge pull request #243 from quartiq/remove-helper-traits
Browse files Browse the repository at this point in the history
remove helper traits
  • Loading branch information
jordens authored Oct 1, 2024
2 parents b46eb71 + 2a6907a commit 774809b
Show file tree
Hide file tree
Showing 27 changed files with 362 additions and 384 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.14.2](https://github.com/quartiq/miniconf/compare/v0.14.1...v0.14.2) - 2024-09-30
## [Unreleased](https://github.com/quartiq/miniconf/compare/v0.14.1...HEAD) - DATE

### Changed

* `Traversal` and `Error` are now `exhaustive`.

### Removed

* The `JsonCoreSlash`/`JsonCoreSlashOwned` and `Postcard`/`PostcardOwned` traits have
been removed in favor of simple functions in the `json::` and `postcard::` modules.

## [0.14.1](https://github.com/quartiq/miniconf/compare/v0.13.0...v0.14.1) - 2024-09-30

### Added
Expand Down
4 changes: 2 additions & 2 deletions miniconf/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "miniconf"
# Sync all crate versions and the py client
version = "0.14.2"
version = "0.14.1"
authors = ["James Irwin <irwineffect@gmail.com>", "Ryan Summers <ryan.summers@vertigo-designs.com>", "Robert Jördens <rj@quartiq.de>"]
edition = "2021"
license = "MIT"
Expand All @@ -12,7 +12,7 @@ categories = ["embedded", "config", "data-structures", "parsing"]

[dependencies]
serde = { version = "1.0.120", default-features = false }
miniconf_derive = { path = "../miniconf_derive", version = "0.14.2", optional = true }
miniconf_derive = { path = "../miniconf_derive", version = "0.14.1", optional = true }
itoa = "1.0.4"
serde-json-core = { version = "0.6.0", optional = true }
postcard = { version = "1.0.8", optional = true }
Expand Down
51 changes: 25 additions & 26 deletions miniconf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ providers are supported.

```rust
use serde::{Deserialize, Serialize};
use miniconf::{Error, JsonCoreSlash, JsonPath, Traversal, Tree, TreeKey, Path, Packed, Node};
use miniconf::{Error, json, JsonPath, Traversal, Tree, TreeKey, Path, Packed, Node};

#[derive(Deserialize, Serialize, Default)]
enum Either {
Expand Down Expand Up @@ -64,56 +64,56 @@ struct Settings {
let mut settings = Settings::default();

// Atomic updates by field name
settings.set_json("/foo", b"true")?;
json::set(&mut settings,"/foo", b"true")?;
assert_eq!(settings.foo, true);
settings.set_json("/enum_", br#""Good""#)?;
settings.set_json("/struct_", br#"{"a": 3, "b": 3}"#)?;
settings.set_json("/array", b"[6, 6]")?;
settings.set_json("/option", b"12")?;
settings.set_json("/option", b"null")?;
json::set(&mut settings, "/enum_", br#""Good""#)?;
json::set(&mut settings, "/struct_", br#"{"a": 3, "b": 3}"#)?;
json::set(&mut settings, "/array", b"[6, 6]")?;
json::set(&mut settings, "/option", b"12")?;
json::set(&mut settings, "/option", b"null")?;

// Exposing nodes of containers
// ... by field name in a struct
settings.set_json("/struct_tree/a", b"4")?;
json::set(&mut settings, "/struct_tree/a", b"4")?;
// ... or by index in an array
settings.set_json("/array_tree/0", b"7")?;
json::set(&mut settings, "/array_tree/0", b"7")?;
// ... or by index and then struct field name
settings.set_json("/array_tree2/0/a", b"11")?;
json::set(&mut settings, "/array_tree2/0/a", b"11")?;
// ... or by hierarchical index
settings.set_json_by_key([7, 0, 1], b"8")?;
json::set_by_key(&mut settings, [7, 0, 1], b"8")?;
// ... or by packed index
let (packed, node) = Settings::transcode::<Packed, _>([7, 1, 0]).unwrap();
assert_eq!(packed.into_lsb().get(), 0b1_0111_1_0);
assert_eq!(node, Node::leaf(3));
settings.set_json_by_key(packed, b"9")?;
json::set_by_key(&mut settings, packed, b"9")?;
// ... or by JSON path
settings.set_json_by_key(&JsonPath(".array_tree2[1].b"), b"10")?;
json::set_by_key(&mut settings, &JsonPath(".array_tree2[1].b"), b"10")?;

// Hiding paths by setting an Option to `None` at runtime
assert_eq!(settings.set_json("/option_tree", b"13"), Err(Traversal::Absent(1).into()));
assert_eq!(json::set(&mut settings, "/option_tree", b"13"), Err(Traversal::Absent(1).into()));
settings.option_tree = Some(0);
settings.set_json("/option_tree", b"13")?;
json::set(&mut settings, "/option_tree", b"13")?;
// Hiding a path and descending into the inner `Tree`
settings.option_tree2 = Some(Inner::default());
settings.set_json("/option_tree2/a", b"14")?;
json::set(&mut settings, "/option_tree2/a", b"14")?;
// Hiding items of an array of `Tree`s
settings.array_option_tree[1] = Some(Inner::default());
settings.set_json("/array_option_tree/1/a", b"15")?;
json::set(&mut settings, "/array_option_tree/1/a", b"15")?;

let mut buf = [0; 16];

// Serializing nodes by path
let len = settings.get_json("/struct_", &mut buf).unwrap();
let len = json::get(&settings, "/struct_", &mut buf).unwrap();
assert_eq!(&buf[..len], br#"{"a":3,"b":3}"#);

// Iterating over all paths
for path in Settings::nodes::<Path<heapless::String<32>, '/'>>() {
let (path, node) = path.unwrap();
assert!(node.is_leaf());
// Serialize each
match settings.get_json(&path, &mut buf) {
match json::get(&settings, &path, &mut buf) {
// Full round-trip: deserialize and set again
Ok(len) => { settings.set_json(&path, &buf[..len])?; }
Ok(len) => { json::set(&mut settings, &path, &buf[..len])?; }
// Some settings are still `None` and thus their paths are expected to be absent
Err(Error::Traversal(Traversal::Absent(_))) => {}
e => { e.unwrap(); }
Expand All @@ -136,11 +136,10 @@ Miniconf is agnostic of the `serde` backend/format, key type/format, and transpo

`miniconf` can be used with any `serde::Serializer`/`serde::Deserializer` backend, and key format.

Support for `/` as the path hierarchy separator and JSON (`serde_json_core`) is implemented
through the [`JsonCoreSlash`] subtrait.
Explicit support for `/` as the path hierarchy separator and JSON (`serde_json_core`) is implemented.

The `Postcard` subtrait supports the `postcard` wire format with any `postcard` flavor and
any [`Keys`] type. Combined with the [`Packed`] key representation, this is a very
Support for the `postcard` wire format with any `postcard` flavor and
any [`Keys`] type is implemented. Combined with the [`Packed`] key representation, this is a very
space-efficient serde-by-key API.

Blanket implementations are provided for all
Expand Down Expand Up @@ -193,9 +192,9 @@ It implements [`Keys`].

## Features

* `json-core`: Enable the [`JsonCoreSlash`] implementation of serializing from and
* `json-core`: Enable helper functions for serializing from and
into json slices (using the `serde_json_core` crate).
* `postcard`: Enable the `Postcard` implementation of serializing from and
* `postcard`: Enable helper functions for serializing from and
into the postcard compact binary format (using the `postcard` crate).
* `derive`: Enable the derive macros in `miniconf_derive`. Enabled by default.

Expand Down
7 changes: 3 additions & 4 deletions miniconf/examples/cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Context;
use miniconf::{Error, JsonCoreSlash, Path, Traversal, TreeKey};
use miniconf::{json, Error, Path, Traversal, TreeKey};

mod common;
use common::Settings;
Expand All @@ -17,15 +17,14 @@ fn main() -> anyhow::Result<()> {
while let Some(key) = args.next() {
let key = key.strip_prefix('-').context("key must start with `-`")?;
let value = args.next().context("missing value")?;
settings
.set_json_by_key(&Path::<_, '-'>(key), value.as_bytes())
json::set_by_key(&mut settings, &Path::<_, '-'>(key), value.as_bytes())
.context("lookup/deserialize")?;
}

// Dump settings
let mut buf = vec![0; 1024];
for (key, _node) in Settings::nodes::<Path<String, '-'>>().map(Result::unwrap) {
match settings.get_json_by_key(&key, &mut buf[..]) {
match json::get_by_key(&settings, &key, &mut buf[..]) {
Ok(len) => {
println!(
"-{} {}",
Expand Down
48 changes: 16 additions & 32 deletions miniconf/examples/menu.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use core::fmt;
use core::marker::PhantomData;

use ::postcard::{de_flavors::Slice as DeSlice, ser_flavors::Slice as SerSlice};
use anyhow::{Context, Result};
use embedded_io::Write;
use embedded_io_async::Write as AWrite;
use postcard::{de_flavors::Slice as DeSlice, ser_flavors::Slice as SerSlice};
use tokio::io::AsyncBufReadExt;

use miniconf::{
Indices, JsonCoreSlashOwned, Keys, Node, Packed, Path, PostcardOwned, Transcode, Traversal,
TreeKey,
json, postcard, Indices, Keys, Node, Packed, Path, Transcode, Traversal, TreeDeserializeOwned,
TreeKey, TreeSerialize,
};

mod common;
Expand Down Expand Up @@ -61,17 +61,14 @@ impl<I> From<usize> for Error<I> {
pub const SEPARATOR: char = '/';

#[derive(Debug, PartialEq, PartialOrd)]
pub struct Menu<M, const Y: usize, const D: usize = Y>
where
M: TreeKey<Y> + ?Sized,
{
pub struct Menu<M, const Y: usize, const D: usize = Y> {
key: Packed,
_m: PhantomData<M>,
}

impl<M, const Y: usize> Default for Menu<M, Y>
where
M: TreeKey<Y> + ?Sized,
M: TreeKey<Y> + TreeSerialize<Y> + TreeDeserializeOwned<Y> + Default,
{
fn default() -> Self {
Self::new(Packed::default())
Expand All @@ -80,7 +77,7 @@ where

impl<M, const Y: usize> Menu<M, Y>
where
M: TreeKey<Y> + ?Sized,
M: TreeKey<Y> + TreeSerialize<Y> + TreeDeserializeOwned<Y> + Default,
{
pub fn new(key: Packed) -> Self {
Self {
Expand Down Expand Up @@ -133,45 +130,36 @@ where
&self,
instance: &M,
buf: &mut [u8],
) -> Result<usize, miniconf::Error<serde_json_core::ser::Error>>
where
M: JsonCoreSlashOwned<Y>,
{
instance.get_json_by_key(self.key, buf)
) -> Result<usize, miniconf::Error<serde_json_core::ser::Error>> {
json::get_by_key(instance, self.key, buf)
}

pub fn set(
&mut self,
instance: &mut M,
buf: &[u8],
) -> Result<usize, miniconf::Error<serde_json_core::de::Error>>
where
M: JsonCoreSlashOwned<Y>,
{
instance.set_json_by_key(self.key, buf)
) -> Result<usize, miniconf::Error<serde_json_core::de::Error>> {
json::set_by_key(instance, self.key, buf)
}

pub fn reset(
&mut self,
instance: &mut M,
buf: &mut [u8],
) -> Result<(), miniconf::Error<postcard::Error>>
where
M: PostcardOwned<Y> + Default,
{
) -> Result<(), miniconf::Error<::postcard::Error>> {
let def = M::default();
for keys in M::nodes::<Packed>().root(self.key)? {
// Slight abuse of TooLong for "keys to long for packed"
let (keys, node) =
keys.map_err(|depth| miniconf::Error::Traversal(Traversal::TooLong(depth)))?;
debug_assert!(node.is_leaf());
let val = match def.get_postcard_by_key(keys, SerSlice::new(buf)) {
let val = match postcard::get_by_key(&def, keys, SerSlice::new(buf)) {
Err(miniconf::Error::Traversal(Traversal::Absent(_))) => {
continue;
}
ret => ret?,
};
let _rest = match instance.set_postcard_by_key(keys, DeSlice::new(val)) {
let _rest = match postcard::set_by_key(instance, keys, DeSlice::new(val)) {
Err(miniconf::Error::Traversal(Traversal::Absent(_))) => {
continue;
}
Expand All @@ -189,7 +177,6 @@ where
) -> Result<(), Error<W::Error>>
where
W: AWrite,
M: JsonCoreSlashOwned<Y> + Default,
{
let def = M::default();
let bl = buf.len();
Expand All @@ -200,7 +187,7 @@ where
awrite(&mut write, ">\n".as_bytes()).await?;
for keys in M::nodes::<Packed>().root(self.key)? {
let (keys, node) = keys?;
let (val, rest) = match instance.get_json_by_key(keys, &mut buf[..]) {
let (val, rest) = match json::get_by_key(instance, keys, &mut buf[..]) {
Err(miniconf::Error::Traversal(Traversal::TooShort(_))) => {
debug_assert!(!node.is_leaf());
("...\n".as_bytes(), &mut buf[..])
Expand All @@ -223,7 +210,7 @@ where
awrite(&mut write, &rest[root_len..path_len]).await?;
awrite(&mut write, ": ".as_bytes()).await?;
awrite(&mut write, val).await?;
let def = match def.get_json_by_key(keys, &mut buf[..]) {
let def = match json::get_by_key(&def, keys, &mut buf[..]) {
Err(miniconf::Error::Traversal(Traversal::TooShort(_depth))) => {
debug_assert!(!node.is_leaf());
continue;
Expand All @@ -248,10 +235,7 @@ where
mut stdout: impl AWrite,
buf: &mut [u8],
instance: &mut M,
) -> anyhow::Result<String>
where
M: JsonCoreSlashOwned<Y> + Default,
{
) -> anyhow::Result<String> {
let mut args = line.splitn(2, ' ');
Ok(match args.next().context("command")? {
"enter" => {
Expand Down
Loading

0 comments on commit 774809b

Please sign in to comment.