Skip to content

Commit

Permalink
Merge pull request #242 from quartiq/exhaustive
Browse files Browse the repository at this point in the history
exhaustive
  • Loading branch information
jordens authored Sep 30, 2024
2 parents 1141cf1 + f089212 commit b46eb71
Show file tree
Hide file tree
Showing 23 changed files with 122 additions and 264 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 2 additions & 6 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.1"
version = "0.14.2"
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.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 }
Expand Down Expand Up @@ -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"]
Expand Down
9 changes: 6 additions & 3 deletions miniconf/examples/cli.rs
Original file line number Diff line number Diff line change
@@ -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();

Expand All @@ -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")?;
}

Expand Down
1 change: 0 additions & 1 deletion miniconf/examples/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ impl<I> From<miniconf::Error<serde_json_core::ser::Error>> for Error<I> {
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!(),
}
}
}
Expand Down
16 changes: 7 additions & 9 deletions miniconf/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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),
Expand All @@ -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 reach 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}")
}
}
}
Expand Down Expand Up @@ -97,7 +96,6 @@ impl Traversal {
}

/// Compound errors
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Error<E> {
/// Tree traversal error
Expand Down
2 changes: 1 addition & 1 deletion miniconf/src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(),
};
}
Expand Down
5 changes: 4 additions & 1 deletion miniconf/src/jsonpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ impl<T: Write + ?Sized> Transcode for JsonPath<T> {
{
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('[')
Expand Down
5 changes: 3 additions & 2 deletions miniconf/src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
58 changes: 43 additions & 15 deletions miniconf/tests/arrays.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -18,26 +23,54 @@ struct Settings {
aam: [[Inner; 2]; 2],
}

fn set_get(
tree: &mut Settings,
path: &str,
value: &[u8],
) -> Result<usize, Error<serde_json_core::de::Error>> {
// 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);
}

Expand Down Expand Up @@ -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::<S, 1>(), [""; 0]);
assert_eq!(paths::<[[S; 0]; 0], 3>(), [""; 0]);

#[derive(Tree)]
struct Q {
Expand All @@ -122,5 +149,6 @@ fn empty() {
#[tree(depth = 1)]
b: [S; 0],
}
assert!(Q::nodes::<()>().exact_size().next().is_none());

assert_eq!(paths::<Q, 3>(), [""; 0]);
}
6 changes: 3 additions & 3 deletions miniconf/tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use miniconf::{JsonCoreSlashOwned, Path, TreeKey};
use miniconf::{JsonCoreSlash, Path, TreeKey};

pub fn paths<M, const Y: usize>() -> Vec<String>
where
Expand All @@ -15,9 +15,9 @@ where
.collect()
}

pub fn set_get<M, const Y: usize>(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<Y>,
M: JsonCoreSlash<'de, Y>,
{
s.set_json(path, value).unwrap();
let mut buf = vec![0; value.len()];
Expand Down
2 changes: 2 additions & 0 deletions miniconf/tests/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ fn enum_skip() {
A(i32, #[tree(skip)] i32),
#[tree(skip)]
B(S),
C,
D,
}
assert_eq!(paths::<E, 1>(), ["/A"]);
}
Expand Down
Loading

0 comments on commit b46eb71

Please sign in to comment.