Skip to content

Commit

Permalink
Merge pull request #160 from quartiq/path
Browse files Browse the repository at this point in the history
add `traverse_by_key` and `{get,set}_by_key` methods and refactor several things
  • Loading branch information
ryan-summers authored Jul 28, 2023
2 parents 4f0a6aa + 939703e commit 2f91ecf
Show file tree
Hide file tree
Showing 23 changed files with 990 additions and 841 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ jobs:
- 1.65.0 # keep in sync with manifest MSRV
args:
- ""
- --all-features
- --no-default-features
- --no-default-features --features json-core

Expand Down
21 changes: 13 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* `json` feature to enable the `Serde<JsonCoreSlash>` spec. Enabled by default.
* Traversal by names or indices has been added through `Miniconf::traverse_by_key()`.

### Changed

* [breaking] The `Miniconf` trait is now generic over the `Deserializer`/`Serializer`. It
doesn't enforce `serde-json-core` or `u8` buffers or `/` as the path hierarchy
separator anymore.
* [breaking] `MiniconfIter` takes the path hierarchy separator from `SerDe` and passes it on to
`Miniconf::next_path` and `Miniconf::metadata`.
* [breaking] `Miniconf::iter_paths()` takes the path hierarchy separator and passes
it on to `Miniconf::path()` and `Metadata::separator()`.
* [breaking] The `Miniconf` trait has been stripped of the provided functions that depended
on the `serde`-backend and path hierarchy separator. Those have been
moved into a super trait `SerDe<S>` that is generic over a specification marker
struct `S`. `SerDe<JsonCoreSlash>` has been implemented for all `Miniconf`
moved into the `JsonCoreSlash` trait that has been implemented for all `Miniconf`
to provide the previously existing functionality.
* The only required change for most downstream crates to adapt to the above is to
make sure the `SerDe` trait is in scope (`use miniconf::SerDe`).
make sure the `JsonCoreSlash` trait is in scope (`use miniconf::JsonCoreSlash`).
* [breaking] Paths now start with the path separator (unless they are empty).
This affects the `Miniconf` derive macro and the `Miniconf` implementation pairs
for `Option`/`Array`.
Downstram crates should ensure non-empty paths start with the separator and
expect `next_path` paths to start with the separator or be empty.
* `set()` and `get()` have been renamed to `set_json()` and `get_json()` respectively to
avoid overlap.
* The path iterator does not need to be `Peekable` anymore.
* [breaking] `iter_paths`/`MiniconfIter` is now generic over the type
* [breaking] `iter_paths`/`PathIter` is now generic over the type
to write the path into. Downstream crates should replace `iter_paths::<L, TS>()` with
`iter_paths::<L, heapless::String<TS>>()`.
e.g. `iter_paths::<L, heapless::String<TS>>()`.
* [breaking] Re-exports of `heapless` and `serde-json-core` have been removed as they are
not needed to work with the public API.
* [breaking] Metadata is now computed by default without taking account path separators.
These can be included using `Metadata::separator()`.
* The main serialization/deserialization methods are now `Miniconf::{set,get}_by_key()`
They are generic over the key iterator `Iterator<Item: miniconf::Key>`.

## [0.7.1] (https://github.com/quartiq/miniconf/compare/v0.7.0...v0.7.1)

Expand Down
9 changes: 3 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ version = "0.7.1"
authors = ["James Irwin <[email protected]>", "Ryan Summers <[email protected]", "Robert Jördens <[email protected]>"]
edition = "2021"
license = "MIT"
description = "Inspect serde namespaces by path"
description = "Serialize/deserialize/access Inspect serde namespaces by path"
repository = "https://github.com/quartiq/miniconf"
keywords = ["settings", "serde", "no_std", "json", "mqtt"]
categories = ["no-std", "config", "rust-patterns", "parsing"]
Expand All @@ -17,9 +17,7 @@ serde = { version = "1.0.120", features = ["derive"], default-features = false }
miniconf_derive = { path = "miniconf_derive" , version = "0.6" }
itoa = "1.0.4"

serde-json-core = { git = "https://github.com/rust-embedded-community/serde-json-core.git" , optional = true }
serde_json = { version = "1.0", optional = true }
tinyvec = { version = "1.6", optional = true }
serde-json-core = { version = "0.5.1" , optional = true }
log = {version = "0.4", optional = true }
heapless = { version = "0.7", optional=true }
minimq = { version = "0.7", optional = true }
Expand All @@ -28,9 +26,8 @@ smlang = { version = "0.6", optional = true }
[features]
default = ["mqtt-client"]
json-core = ["dep:serde-json-core"]
json = ["dep:serde_json", "dep:tinyvec", "std"]
std = []
mqtt-client = ["json-core", "dep:minimq", "dep:smlang", "dep:log", "dep:heapless"]
std = []

[dev-dependencies]
machine = "0.3"
Expand Down
52 changes: 26 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ client](MqttClient) and a Python reference implementation to ineract with it.

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

#[derive(Deserialize, Serialize, Copy, Clone, Default)]
Expand Down Expand Up @@ -67,45 +67,47 @@ let mut settings = Settings::default();
let mut buf = [0; 64];

// Atomic updates by field name
settings.set("/foo", b"true")?;
settings.set_json("/foo", b"true")?;
assert_eq!(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")?;
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")?;

// Deep access by field name in a struct
settings.set("/struct_defer/a", b"4")?;
settings.set_json("/struct_defer/a", b"4")?;
// ... or by index in an array
settings.set("/array_defer/0", b"7")?;
settings.set_json("/array_defer/0", b"7")?;
// ... or by index and then struct field name
settings.set("/array_miniconf/1/b", b"11")?;
settings.set_json("/array_miniconf/1/b", b"11")?;

// If a deferred Option is `None` it is hidden at runtime and can't be accessed
settings.option_defer = None;
assert_eq!(settings.set("/option_defer", b"13"), Err(Error::PathAbsent));
assert_eq!(settings.set_json("/option_defer", b"13"), Err(Error::Absent(1)));
settings.option_defer = Some(0);
settings.set("/option_defer", b"13")?;
settings.set_json("/option_defer", b"13")?;
settings.option_miniconf = Some(Inner::default()).into();
settings.set("/option_miniconf/a", b"14")?;
settings.set_json("/option_miniconf/a", b"14")?;
settings.array_option_miniconf[1] = Some(Inner::default()).into();
settings.set("/array_option_miniconf/1/a", b"15")?;
settings.set_json("/array_option_miniconf/1/a", b"15")?;

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

// Iterating over and serializing all paths
for path in Settings::iter_paths::<3, String>().unwrap() {
match settings.get(&path, &mut buf) {
// Iterating over all paths
for path in Settings::iter_paths::<3, String>("/").unwrap() {
// Serializing each
match settings.get_json(&path, &mut buf) {
Ok(len) => {
settings.set(&path, &buf[..len]).unwrap();
// Deserialize again
settings.set_json(&path, &buf[..len])?;
}
// Some settings are still `None` and thus their paths are expected to be absent
Err(Error::PathAbsent) => {}
Err(Error::Absent(_)) => {}
e => {
e.unwrap();
e?;
}
}
}
Expand Down Expand Up @@ -148,7 +150,7 @@ Miniconf is generic over the `serde` backend/payload format and the path hierarc
(as long as the path can be split by it unambiguously).

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

## Transport
Miniconf is designed to be protocol-agnostic. Any means that can receive key-value input from
Expand All @@ -161,9 +163,7 @@ Deferred (non-atomic) access to inner elements of some types is not yet supporte

## Features
* `mqtt-client` Enable the MQTT client feature. See the example in [MqttClient].
* `json-core` Enable the [SerDe] implementation for the [JsonCoreSlash] style
(using `serde_json_core`).
* `json` Enable the [SerDe] implementation for the [JsonSlash] style (using
`serde_json`).
* `json-core` Enable the [JsonCoreSlash] implementation of serializing from and
into json slices (using `serde_json_core`).

The `mqtt-client` and `json-core` features are enabled by default.
8 changes: 4 additions & 4 deletions examples/readback.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use miniconf::{Miniconf, SerDe};
use miniconf::{JsonCoreSlash, Miniconf};

#[derive(Debug, Default, Miniconf)]
struct AdditionalSettings {
Expand All @@ -23,12 +23,12 @@ fn main() {
};

// Maintains our state of iteration.
let mut settings_iter = Settings::iter_paths::<5, String>().unwrap();
let mut settings_iter = Settings::iter_paths::<5, String>("/").unwrap();

// Just get one topic/value from the iterator
if let Some(topic) = settings_iter.next() {
let mut value = [0; 256];
let len = s.get(&topic, &mut value).unwrap();
let len = s.get_json(&topic, &mut value).unwrap();
println!(
"{:?}: {:?}",
topic,
Expand All @@ -42,7 +42,7 @@ fn main() {

for topic in settings_iter {
let mut value = [0; 256];
let len = s.get(&topic, &mut value).unwrap();
let len = s.get_json(&topic, &mut value).unwrap();
println!(
"{:?}: {:?}",
topic,
Expand Down
10 changes: 5 additions & 5 deletions miniconf_derive/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ use syn::{parse_quote, Generics};

pub struct StructField {
pub field: syn::Field,
pub deferred: bool,
pub defer: bool,
}

impl StructField {
pub fn new(field: syn::Field) -> Self {
let mut deferred = false;
let mut defer = false;

for attr in field.attrs.iter() {
if attr.path().is_ident("miniconf") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("defer") {
deferred = true;
defer = true;
Ok(())
} else {
Err(meta.error(format!("unrecognized miniconf attribute {:?}", meta.path)))
Expand All @@ -23,7 +23,7 @@ impl StructField {
}
}

Self { deferred, field }
Self { defer, field }
}

fn bound_type(&self, ident: &syn::Ident, generics: &mut Generics, array: bool) {
Expand All @@ -32,7 +32,7 @@ impl StructField {
if type_param.ident == *ident {
// Deferred array types are a special case. These types defer directly into a
// manual implementation of Miniconf that calls serde functions directly.
if self.deferred && !array {
if self.defer && !array {
// For deferred, non-array data types, we will recursively call into
// Miniconf trait functions.
type_param.bounds.push(parse_quote!(miniconf::Miniconf));
Expand Down
Loading

0 comments on commit 2f91ecf

Please sign in to comment.