From eb4d328f5db71216ae5efa6f451e544f6af90530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 30 Sep 2024 16:00:59 +0200 Subject: [PATCH 1/3] exhausitve errors, tests cleanup * Make Error and Traversal exhaustive. This did cause a message (error or warning?) in the menu example due to now unreachable code. But semver-check didn't mention anything. * fix some spelling mistakes * remove the indices integration tests as that's now in arrays * increased Packed coverage * use common more in integration tests * rename/clean a ui test * v0.14.2 chores --- CHANGELOG.md | 6 + miniconf/Cargo.toml | 8 +- miniconf/examples/menu.rs | 1 - miniconf/src/error.rs | 16 ++- miniconf/src/jsonpath.rs | 5 +- miniconf/src/tree.rs | 5 +- miniconf/tests/arrays.rs | 58 ++++++--- miniconf/tests/enum.rs | 2 + miniconf/tests/index.rs | 112 ------------------ miniconf/tests/ui/enum-non-newtype.rs | 5 - miniconf/tests/ui/enum-non-newtype.stderr | 8 -- .../ui/{enum-skip.rs => skip-unnamed.rs} | 0 .../{enum-skip.stderr => skip-unnamed.stderr} | 4 +- miniconf_derive/Cargo.toml | 2 +- miniconf_mqtt/README.md | 6 +- 15 files changed, 73 insertions(+), 165 deletions(-) delete mode 100644 miniconf/tests/index.rs rename miniconf/tests/ui/{enum-skip.rs => skip-unnamed.rs} (100%) rename miniconf/tests/ui/{enum-skip.stderr => skip-unnamed.stderr} (77%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 594996cb..778f53f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ 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 + +### Changed + +* `Traversal` and `Error` are now `exhaustive`. + ## [0.14.1](https://github.com/quartiq/miniconf/compare/v0.13.0...v0.14.1) - 2024-09-30 ### Added diff --git a/miniconf/Cargo.toml b/miniconf/Cargo.toml index 9fd79c2d..535baec1 100644 --- a/miniconf/Cargo.toml +++ b/miniconf/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "miniconf" # Sync all crate versions and the py client -version = "0.14.1" +version = "0.14.2" authors = ["James Irwin ", "Ryan Summers ", "Robert Jördens "] edition = "2021" license = "MIT" @@ -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.1", optional = true } +miniconf_derive = { path = "../miniconf_derive", version = "0.14.2", optional = true } itoa = "1.0.4" serde-json-core = { version = "0.6.0", optional = true } postcard = { version = "1.0.8", optional = true } @@ -50,10 +50,6 @@ required-features = ["derive"] name = "generics" required-features = ["json-core", "derive"] -[[test]] -name = "index" -required-features = ["json-core", "derive"] - [[test]] name = "iter" required-features = ["json-core", "derive"] diff --git a/miniconf/examples/menu.rs b/miniconf/examples/menu.rs index da532c84..7fdd2324 100644 --- a/miniconf/examples/menu.rs +++ b/miniconf/examples/menu.rs @@ -48,7 +48,6 @@ impl From> for Error { miniconf::Error::Inner(depth, e) => Self::Serialize(depth, e), miniconf::Error::Traversal(e) => Self::Traversal(e), miniconf::Error::Finalization(e) => Self::Serialize(0, e), - _ => unimplemented!(), } } } diff --git a/miniconf/src/error.rs b/miniconf/src/error.rs index f0bc8916..4305c2fa 100644 --- a/miniconf/src/error.rs +++ b/miniconf/src/error.rs @@ -10,12 +10,11 @@ use core::fmt::{Debug, Display, Formatter}; /// /// If multiple errors are applicable simultaneously the precedence /// is as per the order in the enum definition (from high to low). -#[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Traversal { - /// The does not exist at runtime. + /// A node does not exist at runtime. /// - /// The `enum` variant is currently absent. + /// An `enum` variant in the tree towards the node is currently absent. /// This is for example the case if an [`Option`] using the `Tree*` /// traits is `None` at runtime. See also [`crate::TreeKey#option`]. Absent(usize), @@ -30,7 +29,7 @@ pub enum Traversal { /// The key is too long and goes beyond a leaf node. TooLong(usize), - /// A field could not be accessed. + /// A node could not be accessed. /// /// The `get` or `get_mut` accessor returned an error message. Access(usize, &'static str), @@ -48,19 +47,19 @@ impl Display for Traversal { write!(f, "Variant absent (depth: {depth})") } Traversal::TooShort(depth) => { - write!(f, "Key too short (depth: {depth})") + write!(f, "Key does not read a leaf (depth: {depth})") } Traversal::NotFound(depth) => { write!(f, "Key not found (depth: {depth})") } Traversal::TooLong(depth) => { - write!(f, "Key too long (depth: {depth})") + write!(f, "Key goes beyond a leaf (depth: {depth})") } Traversal::Access(depth, msg) => { - write!(f, "Access failed (depth: {depth}): {msg}") + write!(f, "Node accessor failed (depth: {depth}): {msg}") } Traversal::Invalid(depth, msg) => { - write!(f, "Invalid value (depth: {depth}): {msg}") + write!(f, "Invalid deserialized value (depth: {depth}): {msg}") } } } @@ -97,7 +96,6 @@ impl Traversal { } /// Compound errors -#[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Error { /// Tree traversal error diff --git a/miniconf/src/jsonpath.rs b/miniconf/src/jsonpath.rs index a4576213..7cd7c03a 100644 --- a/miniconf/src/jsonpath.rs +++ b/miniconf/src/jsonpath.rs @@ -132,7 +132,10 @@ impl Transcode for JsonPath { { M::traverse_by_key(keys.into_keys(), |index, name, _len| { match name { - Some(name) => self.0.write_char('.').and_then(|()| self.0.write_str(name)), + Some(name) => { + debug_assert!(!name.contains(['.', '\'', '[', ']'])); + self.0.write_char('.').and_then(|()| self.0.write_str(name)) + } None => self .0 .write_char('[') diff --git a/miniconf/src/tree.rs b/miniconf/src/tree.rs index e4f5a6f8..c789a997 100644 --- a/miniconf/src/tree.rs +++ b/miniconf/src/tree.rs @@ -12,8 +12,9 @@ use crate::{Error, IntoKeys, Keys, Node, NodeIter, Transcode, Traversal}; pub struct Metadata { /// The maximum length of a path in bytes. /// - /// This is the exact maximum of the length of the concatenation of all field names/indices - /// in a path. By default, it does not include separators. + /// This is the exact maximum of the length of the concatenation of the node names + /// in a [`crate::Path`] excluding the separators. See [`Self::max_length()`] for + /// the maximum length including separators. pub max_length: usize, /// The maximum key depth. diff --git a/miniconf/tests/arrays.rs b/miniconf/tests/arrays.rs index 14e22f9e..6dac5502 100644 --- a/miniconf/tests/arrays.rs +++ b/miniconf/tests/arrays.rs @@ -1,4 +1,9 @@ -use miniconf::{Deserialize, JsonCoreSlash, Serialize, Traversal, Tree, TreeKey}; +use miniconf::{ + Deserialize, Error, Indices, JsonCoreSlash, Packed, Path, Serialize, Traversal, Tree, TreeKey, +}; + +mod common; +use common::paths; #[derive(Debug, Copy, Clone, Default, Tree, Deserialize, Serialize)] struct Inner { @@ -18,26 +23,54 @@ struct Settings { aam: [[Inner; 2]; 2], } +fn set_get( + tree: &mut Settings, + path: &str, + value: &[u8], +) -> Result> { + // Path + common::set_get(tree, path, value); + + // Indices + let (idx, node): (Indices<[usize; 4]>, _) = Settings::transcode(&Path::<_, '/'>::from(path))?; + assert!(node.is_leaf()); + let idx = Indices::from(&idx[..node.depth()]); + tree.set_json_by_key(&idx, value)?; + let mut buf = vec![0; value.len()]; + let len = tree.get_json_by_key(&idx, &mut buf[..]).unwrap(); + assert_eq!(&buf[..len], value); + + // Packed + let (idx, node): (Packed, _) = Settings::transcode(&idx)?; + assert!(node.is_leaf()); + tree.set_json_by_key(idx, value)?; + let mut buf = vec![0; value.len()]; + let len = tree.get_json_by_key(idx, &mut buf[..]).unwrap(); + assert_eq!(&buf[..len], value); + + Ok(node.depth()) +} + #[test] fn atomic() { let mut s = Settings::default(); - s.set_json("/a", b"[1,2]").unwrap(); + set_get(&mut s, "/a", b"[1,2]").unwrap(); assert_eq!(s.a, [1, 2]); } #[test] fn defer() { let mut s = Settings::default(); - s.set_json("/d/1", b"99").unwrap(); + set_get(&mut s, "/d/1", b"99").unwrap(); assert_eq!(s.d[1], 99); } #[test] fn defer_miniconf() { let mut s = Settings::default(); - s.set_json("/am/0/c", b"1").unwrap(); + set_get(&mut s, "/am/0/c", b"1").unwrap(); assert_eq!(s.am[0].c, 1); - s.set_json("/aam/0/0/c", b"3").unwrap(); + set_get(&mut s, "/aam/0/0/c", b"3").unwrap(); assert_eq!(s.aam[0][0].c, 3); } @@ -101,19 +134,13 @@ fn metadata() { #[test] fn empty() { - assert!(<[u32; 0]>::nodes::<()>().exact_size().next().is_none()); + assert_eq!(paths::<[u32; 0], 1>(), [""; 0]); #[derive(Tree, Serialize, Deserialize)] struct S {} - assert!(<[S; 0] as TreeKey>::nodes::<()>() - .exact_size() - .next() - .is_none()); - assert!(<[[S; 0]; 0] as TreeKey>::nodes::<()>() - .exact_size() - .next() - .is_none()); + assert_eq!(paths::(), [""; 0]); + assert_eq!(paths::<[[S; 0]; 0], 3>(), [""; 0]); #[derive(Tree)] struct Q { @@ -122,5 +149,6 @@ fn empty() { #[tree(depth = 1)] b: [S; 0], } - assert!(Q::nodes::<()>().exact_size().next().is_none()); + + assert_eq!(paths::(), [""; 0]); } diff --git a/miniconf/tests/enum.rs b/miniconf/tests/enum.rs index 09f798f8..eca4896f 100644 --- a/miniconf/tests/enum.rs +++ b/miniconf/tests/enum.rs @@ -71,6 +71,8 @@ fn enum_skip() { A(i32, #[tree(skip)] i32), #[tree(skip)] B(S), + C, + D, } assert_eq!(paths::(), ["/A"]); } diff --git a/miniconf/tests/index.rs b/miniconf/tests/index.rs deleted file mode 100644 index cdf1f9c4..00000000 --- a/miniconf/tests/index.rs +++ /dev/null @@ -1,112 +0,0 @@ -// This follows arrays.rs, just with indices. - -use miniconf::{Deserialize, JsonCoreSlash, Serialize, Traversal, Tree}; - -#[derive(Debug, Copy, Clone, Default, Tree, Deserialize, Serialize)] -struct Inner { - c: u8, -} - -#[derive(Debug, Default, Tree)] -struct Settings { - a: [u8; 2], - #[tree(depth = 1)] - d: [u8; 2], - #[tree(depth = 1)] - dm: [Inner; 2], - #[tree(depth = 2)] - am: [Inner; 2], - #[tree(depth = 3)] - aam: [[Inner; 2]; 2], -} - -#[test] -fn atomic() { - let mut s = Settings::default(); - s.set_json_by_key([0], b"[1,2]").unwrap(); - assert_eq!(s.a, [1, 2]); -} - -#[test] -fn defer() { - let mut s = Settings::default(); - s.set_json_by_key([1, 1], b"99").unwrap(); - assert_eq!(s.d, [0, 99]); -} - -#[test] -fn defer_miniconf() { - let mut s = Settings::default(); - s.set_json_by_key([3, 0, 0], b"1").unwrap(); - assert_eq!(s.am[0].c, 1); - s.set_json_by_key([4, 0, 0, 0], b"3").unwrap(); - assert_eq!(s.aam[0][0].c, 3); -} - -#[test] -fn too_short() { - let mut s = Settings::default(); - assert_eq!( - s.set_json_by_key([1], b"[1,2,3]"), - Err(Traversal::TooShort(1).into()) - ); -} - -#[test] -fn too_long() { - assert_eq!( - Settings::default().set_json_by_key([0, 1], b"7"), - Err(Traversal::TooLong(1).into()) - ); - assert_eq!( - Settings::default().set_json_by_key([1, 0, 2], b"7"), - Err(Traversal::TooLong(2).into()) - ); - assert_eq!( - Settings::default().set_json_by_key([2, 0, 0], b"7"), - Err(Traversal::TooLong(2).into()) - ); - assert_eq!( - Settings::default().set_json_by_key([2, 0, 1], b"7"), - Err(Traversal::TooLong(2).into()) - ); -} - -#[test] -fn not_found() { - assert_eq!( - Settings::default().set_json_by_key([1, 3], b"7"), - Err(Traversal::NotFound(2).into()) - ); - assert_eq!( - Settings::default().set_json_by_key([5], b"7"), - Err(Traversal::NotFound(1).into()) - ); - assert_eq!( - Settings::default().set_json_by_key([4, 0, 0, 1], b"7"), - Err(Traversal::NotFound(4).into()) - ); -} - -#[test] -fn empty() { - assert!([0u32; 0].set_json_by_key([0usize; 0], b"").is_err()); - - #[derive(Tree, Serialize, Deserialize, Copy, Clone, Default)] - struct S {} - - let mut s = [S::default(); 0]; - assert!(JsonCoreSlash::<1>::set_json_by_key(&mut s, [0usize; 0], b"1").is_err()); - - let mut s = [[S::default(); 0]; 0]; - assert!(JsonCoreSlash::<1>::set_json_by_key(&mut s, [0usize; 0], b"1").is_err()); - - #[derive(Tree, Default)] - struct Q { - #[tree(depth = 2)] - a: [S; 0], - #[tree(depth = 1)] - b: [S; 0], - } - assert!(Q::default().set_json_by_key([0usize; 0], b"").is_err()); -} diff --git a/miniconf/tests/ui/enum-non-newtype.rs b/miniconf/tests/ui/enum-non-newtype.rs index 45b57588..fda2c3d3 100644 --- a/miniconf/tests/ui/enum-non-newtype.rs +++ b/miniconf/tests/ui/enum-non-newtype.rs @@ -5,9 +5,4 @@ pub enum E1 { A(i32, i32), } -#[derive(Tree)] -pub enum E2 { - A { a: i32, b: i32 }, -} - fn main() {} diff --git a/miniconf/tests/ui/enum-non-newtype.stderr b/miniconf/tests/ui/enum-non-newtype.stderr index f40b96e2..6cd5bced 100644 --- a/miniconf/tests/ui/enum-non-newtype.stderr +++ b/miniconf/tests/ui/enum-non-newtype.stderr @@ -3,11 +3,3 @@ error: Only newtype (single field tuple) enum variants are supported. | 5 | A(i32, i32), | ^ - -error: Unsupported shape `named fields`. Expected unnamed fields or no fields. - --> tests/ui/enum-non-newtype.rs:8:10 - | -8 | #[derive(Tree)] - | ^^^^ - | - = note: this error originates in the derive macro `Tree` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/miniconf/tests/ui/enum-skip.rs b/miniconf/tests/ui/skip-unnamed.rs similarity index 100% rename from miniconf/tests/ui/enum-skip.rs rename to miniconf/tests/ui/skip-unnamed.rs diff --git a/miniconf/tests/ui/enum-skip.stderr b/miniconf/tests/ui/skip-unnamed.stderr similarity index 77% rename from miniconf/tests/ui/enum-skip.stderr rename to miniconf/tests/ui/skip-unnamed.stderr index 661cac0f..7c5e141e 100644 --- a/miniconf/tests/ui/enum-skip.stderr +++ b/miniconf/tests/ui/skip-unnamed.stderr @@ -1,11 +1,11 @@ error: Can only `skip` terminal tuple struct fields - --> tests/ui/enum-skip.rs:4:21 + --> tests/ui/skip-unnamed.rs:4:21 | 4 | pub struct S(#[tree(skip)] i32, i32); | ^^^^ error: Can only `skip` terminal tuple struct fields - --> tests/ui/enum-skip.rs:8:14 + --> tests/ui/skip-unnamed.rs:8:14 | 8 | A(#[tree(skip)] i32, i32), | ^^^^ diff --git a/miniconf_derive/Cargo.toml b/miniconf_derive/Cargo.toml index e807f887..4d945c8d 100644 --- a/miniconf_derive/Cargo.toml +++ b/miniconf_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miniconf_derive" -version = "0.14.1" +version = "0.14.2" authors = ["James Irwin ", "Ryan Summers ", "Robert Jördens "] edition = "2021" license = "MIT" diff --git a/miniconf_mqtt/README.md b/miniconf_mqtt/README.md index 253e1c29..02876a15 100644 --- a/miniconf_mqtt/README.md +++ b/miniconf_mqtt/README.md @@ -8,10 +8,10 @@ This package contains a MQTT client exposing a [`miniconf`](https://crates.io/cr | --- | --- | --- | --- | | Get | Leaf | set | empty | | List | Internal | set | empty | -| Dump | | not set | empty | +| Dump | (any) | not set | empty | | Set | Leaf | | some | -| Error | Internal | | some | +| (Error) | Internal | | some | ## Notes -* A list command will also list paths that are absent at runtime. +* `List` list paths that would result in `miniconf::Traversal::Absent` on `Get` or `Set`. From 73ab4718379c1d4ee2a9861c11adcce73dec4225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 30 Sep 2024 16:23:01 +0200 Subject: [PATCH 2/3] tests: more common --- miniconf/tests/common/mod.rs | 6 ++--- miniconf/tests/option.rs | 42 +++++++++--------------------- miniconf/tests/skipped.rs | 9 ------- miniconf/tests/structs.rs | 50 +++++++++++------------------------- 4 files changed, 30 insertions(+), 77 deletions(-) diff --git a/miniconf/tests/common/mod.rs b/miniconf/tests/common/mod.rs index d21cd196..05e92e59 100644 --- a/miniconf/tests/common/mod.rs +++ b/miniconf/tests/common/mod.rs @@ -1,4 +1,4 @@ -use miniconf::{JsonCoreSlashOwned, Path, TreeKey}; +use miniconf::{JsonCoreSlash, Path, TreeKey}; pub fn paths() -> Vec where @@ -15,9 +15,9 @@ where .collect() } -pub fn set_get(s: &mut M, path: &str, value: &[u8]) +pub fn set_get<'de, M, const Y: usize>(s: &mut M, path: &str, value: &'de [u8]) where - M: JsonCoreSlashOwned, + M: JsonCoreSlash<'de, Y>, { s.set_json(path, value).unwrap(); let mut buf = vec![0; value.len()]; diff --git a/miniconf/tests/option.rs b/miniconf/tests/option.rs index ed353b3a..a2b5a2be 100644 --- a/miniconf/tests/option.rs +++ b/miniconf/tests/option.rs @@ -1,4 +1,7 @@ -use miniconf::{Error, JsonCoreSlash, Path, Traversal, Tree, TreeKey}; +use miniconf::{Error, JsonCoreSlash, Traversal, Tree}; + +mod common; +use common::*; #[derive(PartialEq, Debug, Clone, Default, Tree)] struct Inner { @@ -11,26 +14,9 @@ struct Settings { value: Option, } -fn nodes, const Y: usize>(want: &[&str]) { - assert_eq!( - M::nodes::>() - .exact_size() - .map(|pn| { - let (p, n) = pn.unwrap(); - assert!(n.is_leaf()); - assert_eq!(p.chars().filter(|c| *c == p.separator()).count(), n.depth()); - p.into_inner() - }) - .collect::>(), - want - ); -} - #[test] fn just_option() { - let mut it = Option::::nodes::>().exact_size(); - assert_eq!(it.next().unwrap().unwrap().0.as_str(), ""); - assert_eq!(it.next(), None); + assert_eq!(paths::, 1>(), [""]); } #[test] @@ -58,21 +44,17 @@ fn option_get_set_none() { #[test] fn option_get_set_some() { let mut settings = Settings::default(); - let mut data = [0; 10]; // Check that if the option is Some, the value can be get or set. settings.value.replace(Inner { data: 5 }); - let len = settings.get_json("/value/data", &mut data).unwrap(); - assert_eq!(&data[..len], b"5"); - - settings.set_json("/value/data", b"7").unwrap(); + set_get(&mut settings, "/value/data", b"7"); assert_eq!(settings.value.unwrap().data, 7); } #[test] fn option_iterate_some_none() { - nodes::(&["/value/data"]); + assert_eq!(paths::(), ["/value/data"]); } #[test] @@ -81,15 +63,15 @@ fn option_test_normal_option() { struct S { data: Option, } - nodes::(&["/data"]); + assert_eq!(paths::(), ["/data"]); let mut s = S::default(); assert!(s.data.is_none()); - s.set_json("/data", b"7").unwrap(); + set_get(&mut s, "/data", b"7"); assert_eq!(s.data, Some(7)); - s.set_json("/data", b"null").unwrap(); + set_get(&mut s, "/data", b"null"); assert!(s.data.is_none()); } @@ -100,14 +82,14 @@ fn option_test_defer_option() { #[tree(depth = 1)] data: Option, } - nodes::(&["/data"]); + assert_eq!(paths::(), ["/data"]); let mut s = S::default(); assert!(s.data.is_none()); assert!(s.set_json("/data", b"7").is_err()); s.data = Some(0); - s.set_json("/data", b"7").unwrap(); + set_get(&mut s, "/data", b"7"); assert_eq!(s.data, Some(7)); assert!(s.set_json("/data", b"null").is_err()); diff --git a/miniconf/tests/skipped.rs b/miniconf/tests/skipped.rs index ed42f71b..cf34109a 100644 --- a/miniconf/tests/skipped.rs +++ b/miniconf/tests/skipped.rs @@ -31,15 +31,6 @@ fn path() { ); } -#[test] -fn skip_enum() { - #[allow(dead_code)] - #[derive(Tree)] - pub enum E { - A(i32, #[tree(skip)] i32), - } -} - #[test] fn skip_struct() { #[allow(dead_code)] diff --git a/miniconf/tests/structs.rs b/miniconf/tests/structs.rs index 0fb0c9c3..d8db57bc 100644 --- a/miniconf/tests/structs.rs +++ b/miniconf/tests/structs.rs @@ -1,7 +1,10 @@ use miniconf::{ - Deserialize, JsonCoreSlash, Path, Serialize, Tree, TreeDeserialize, TreeKey, TreeSerialize, + Deserialize, JsonCoreSlash, Serialize, Tree, TreeDeserialize, TreeKey, TreeSerialize, }; +mod common; +use common::*; + #[test] fn structs() { #[derive(Serialize, Deserialize, Tree, Default, PartialEq, Debug)] @@ -24,10 +27,10 @@ fn structs() { assert!(settings.set_json("/c/a", b"4").is_err()); // Inner settings can be updated atomically. - settings.set_json("/c", b"{\"a\": 5}").unwrap(); + set_get(&mut settings, "/c", b"{\"a\":5}"); // Deferred inner settings can be updated individually. - settings.set_json("/d/a", b"3").unwrap(); + set_get(&mut settings, "/d/a", b"3"); // It is not allowed to set a non-terminal node. assert!(settings.set_json("/d", b"{\"a\": 5").is_err()); @@ -41,43 +44,28 @@ fn structs() { assert_eq!(metadata.max_length("/"), "/d/a".len()); assert_eq!(metadata.count, 4); - assert_eq!( - Settings::nodes::>() - .exact_size() - .map(|p| p.unwrap().0.into_inner()) - .collect::>(), - vec!["/a", "/b", "/c", "/d/a"] - ); + assert_eq!(paths::(), ["/a", "/b", "/c", "/d/a"]); } #[test] fn empty_struct() { #[derive(Tree, Default)] struct Settings {} - assert!(Settings::nodes::>() - .exact_size() - .next() - .is_none()); + assert_eq!(paths::(), [""; 0]); } #[test] fn unit_struct() { #[derive(Tree, Default)] struct Settings; - assert!(Settings::nodes::>() - .exact_size() - .next() - .is_none()); + assert_eq!(paths::(), [""; 0]); } #[test] fn empty_tuple_struct() { #[derive(Tree, Default)] struct Settings(); - assert!(Settings::nodes::>() - .exact_size() - .next() - .is_none()); + assert_eq!(paths::(), [""; 0]); } #[test] @@ -88,7 +76,7 @@ fn borrowed() { a: &'a str, } let mut s = S { a: "foo" }; - s.set_json("/a", br#""bar""#).unwrap(); + set_get(&mut s, "/a", br#""bar""#); assert_eq!(s.a, "bar"); } @@ -99,20 +87,12 @@ fn tuple_struct() { let mut s = Settings::default(); - let mut buf = [0u8; 256]; - let len = s.get_json("/0", &mut buf).unwrap(); - assert_eq!(&buf[..len], b"0"); - - s.set_json("/1", b"3.0").unwrap(); + set_get(&mut s, "/0", br#"2"#); + assert_eq!(s.0, 2); + set_get(&mut s, "/1", br#"3.0"#); assert_eq!(s.1, 3.0); s.set_json("/2", b"3.0").unwrap_err(); s.set_json("/foo", b"3.0").unwrap_err(); - assert_eq!( - Settings::nodes::>() - .exact_size() - .map(|p| p.unwrap().0.into_inner()) - .collect::>(), - vec!["/0", "/1"] - ); + assert_eq!(paths::(), ["/0", "/1"]); } From f089212f4c2e5898f13ff77882aaa8d18d1bb36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 30 Sep 2024 17:24:37 +0200 Subject: [PATCH 3/3] wording, style, spelling --- miniconf/examples/cli.rs | 9 ++++++--- miniconf/src/error.rs | 2 +- miniconf/src/iter.rs | 2 +- miniconf_mqtt/Cargo.toml | 1 + miniconf_mqtt/src/lib.rs | 29 +++++++++++------------------ 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/miniconf/examples/cli.rs b/miniconf/examples/cli.rs index a2d9c293..dd9149e1 100644 --- a/miniconf/examples/cli.rs +++ b/miniconf/examples/cli.rs @@ -1,10 +1,14 @@ -use anyhow::{Context, Result}; +use anyhow::Context; use miniconf::{Error, JsonCoreSlash, Path, Traversal, TreeKey}; mod common; use common::Settings; -fn main() -> Result<()> { +// Simple command line interface example for miniconf. +// This exposes all leaf nodes as long options, parses the command line, +// and then prints the settings struct as a list of option key-value pairs. + +fn main() -> anyhow::Result<()> { let mut settings = Settings::default(); settings.enable(); @@ -15,7 +19,6 @@ fn main() -> Result<()> { let value = args.next().context("missing value")?; settings .set_json_by_key(&Path::<_, '-'>(key), value.as_bytes()) - .map_err(anyhow::Error::msg) .context("lookup/deserialize")?; } diff --git a/miniconf/src/error.rs b/miniconf/src/error.rs index 4305c2fa..49deadec 100644 --- a/miniconf/src/error.rs +++ b/miniconf/src/error.rs @@ -47,7 +47,7 @@ impl Display for Traversal { write!(f, "Variant absent (depth: {depth})") } Traversal::TooShort(depth) => { - write!(f, "Key does not read a leaf (depth: {depth})") + write!(f, "Key does not reach a leaf (depth: {depth})") } Traversal::NotFound(depth) => { write!(f, "Key not found (depth: {depth})") diff --git a/miniconf/src/iter.rs b/miniconf/src/iter.rs index d36927d0..e321244f 100644 --- a/miniconf/src/iter.rs +++ b/miniconf/src/iter.rs @@ -159,7 +159,7 @@ where Some(Err(depth)) } // TooLong: impossible due to Consume - // Absent, Finalization, Invalid, Access: not returned by traverse(traverse_by_key()) + // Absent, Finalization, Invalid, Access: not returned by transcode (traverse_by_key()) _ => unreachable!(), }; } diff --git a/miniconf_mqtt/Cargo.toml b/miniconf_mqtt/Cargo.toml index 68ee2753..f9c587c3 100644 --- a/miniconf_mqtt/Cargo.toml +++ b/miniconf_mqtt/Cargo.toml @@ -19,6 +19,7 @@ embedded-io = "0.6" log = "0.4" heapless = "0.8" serde-json-core = "0.6.0" +strum = { version = "0.26.3", features = ["derive"] } [[example]] name = "mqtt" diff --git a/miniconf_mqtt/src/lib.rs b/miniconf_mqtt/src/lib.rs index 994a7f6a..e24ddc2f 100644 --- a/miniconf_mqtt/src/lib.rs +++ b/miniconf_mqtt/src/lib.rs @@ -17,6 +17,7 @@ use minimq::{ types::{Properties, SubscriptionOptions, TopicFilter}, ConfigBuilder, DeferredPublication, ProtocolError, Publication, QoS, }; +use strum::IntoStaticStr; use embedded_io::Write; @@ -33,8 +34,6 @@ const DUMP_TIMEOUT_SECONDS: u32 = 2; const SEPARATOR: char = '/'; -type Iter = NodeIter, SEPARATOR>>; - /// Miniconf MQTT joint error type #[derive(Debug, PartialEq)] pub enum Error { @@ -114,7 +113,7 @@ mod sm { /// Cache correlation data and topic for multi-part responses. struct Multipart { - iter: Iter, + iter: NodeIter, SEPARATOR>>, response_topic: Option>, correlation_data: Option>, } @@ -164,7 +163,7 @@ impl, const Y: usize> TryFrom<&minimq::types::Properties<'_>> for } } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, IntoStaticStr)] enum ResponseCode { Ok, Continue, @@ -173,15 +172,9 @@ enum ResponseCode { impl From for minimq::Property<'static> { fn from(value: ResponseCode) -> Self { - let string = match value { - ResponseCode::Ok => "Ok", - ResponseCode::Continue => "Continue", - ResponseCode::Error => "Error", - }; - minimq::Property::UserProperty( minimq::types::Utf8String("code"), - minimq::types::Utf8String(string), + minimq::types::Utf8String(value.into()), ) } } @@ -342,7 +335,7 @@ where } } // All states must handle MQTT traffic. - self.poll(settings).map(|c| c == Changed::Changed) + self.poll(settings).map(|c| c == State::Changed) } fn alive(&mut self) -> Result<(), minimq::PubError> { @@ -500,7 +493,7 @@ where }) } - fn poll(&mut self, settings: &mut Settings) -> Result> { + fn poll(&mut self, settings: &mut Settings) -> Result> { let Self { mqtt, state, @@ -515,7 +508,7 @@ where .map(Path::<_, SEPARATOR>::from) else { info!("Unexpected topic: {topic}"); - return Changed::Unchanged; + return State::Unchanged; }; if payload.is_empty() { @@ -562,7 +555,7 @@ where } } } - Changed::Unchanged + State::Unchanged } else { // Set settings @@ -578,7 +571,7 @@ where minimq::Error::SessionReset => { warn!("Session reset"); self.state.process_event(sm::Events::Reset).unwrap(); - Ok(Changed::Unchanged) + Ok(State::Unchanged) } other => Err(other.into()), }) @@ -586,13 +579,13 @@ where } #[derive(Default, Copy, Clone, PartialEq, PartialOrd)] -enum Changed { +enum State { #[default] Unchanged, Changed, } -impl From for Changed { +impl From for State { fn from(value: bool) -> Self { if value { Self::Changed