From d450aec26234226723ead12bf177a112b44b9526 Mon Sep 17 00:00:00 2001 From: SingularitySurfer Date: Thu, 13 Oct 2022 10:37:50 +0200 Subject: [PATCH 1/6] add string without macro --- src/lib.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index f9c1cd83..9cabd9b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,6 +122,7 @@ mod option; /// Provides iteration utilities over [Miniconf] structures. pub mod iter; +use heapless::String; #[cfg(feature = "mqtt-client")] pub use mqtt_client::MqttClient; @@ -407,3 +408,63 @@ impl_single!(f64); impl_single!(usize); impl_single!(bool); + +impl Miniconf for String { + fn string_set( + &mut self, + mut topic_parts: core::iter::Peekable>, + value: &[u8], + ) -> Result<(), Error> { + if topic_parts.peek().is_some() { + return Err(Error::PathTooLong); + } + *self = serde_json_core::from_slice(value)?.0; + Ok(()) + } + + fn string_get( + &self, + mut topic_parts: core::iter::Peekable>, + value: &mut [u8], + ) -> Result { + if topic_parts.peek().is_some() { + return Err(Error::PathTooLong); + } + + serde_json_core::to_slice(self, value).map_err(|_| Error::SerializationFailed) + } + + fn get_metadata(&self) -> MiniconfMetadata { + MiniconfMetadata { + // No topic length is needed, as there are no sub-members. + max_topic_size: 0, + // One index is required for the current element. + max_depth: 1, + } + } + + // This implementation is the base case for primitives where it will + // yield once for self, then return None on subsequent calls. + fn recurse_paths( + &self, + index: &mut [usize], + _topic: &mut heapless::String, + ) -> Option<()> { + if index.is_empty() { + // Note: During expected execution paths using `iter()`, the size of the + // index stack is checked in advance to make sure this condition doesn't occur. + // However, it's possible to happen if the user manually calls `recurse_paths`. + unreachable!("Index stack too small"); + } + + let i = index[0]; + index[0] += 1; + index[1..].iter_mut().for_each(|x| *x = 0); + + if i == 0 { + Some(()) + } else { + None + } + } +} From 0ac710e2ab3ac77dd41c19674ad1dbf698257760 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Oct 2022 11:16:58 +0200 Subject: [PATCH 2/6] Updating string impl --- CHANGELOG.md | 3 +++ src/lib.rs | 70 +++++++--------------------------------------------- 2 files changed, 12 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ba464ab..d42add0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed * python module: don't emite whitespace in JSON to match serde-json-core (#92) +### Added +* `heapless::String` now implements `Miniconf` directly. + ## [0.5.0] - 2022-05-12 ### Changed diff --git a/src/lib.rs b/src/lib.rs index 9cabd9b0..3610bb63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -331,6 +331,11 @@ pub trait Miniconf { macro_rules! impl_single { ($x:ty) => { impl Miniconf for $x { + impl_single!(); + } + }; + + () => { fn string_set( &mut self, mut topic_parts: core::iter::Peekable>, @@ -388,10 +393,13 @@ macro_rules! impl_single { None } } - } }; } +impl Miniconf for String { + impl_single!(); +} + // Implement trait for the primitive types impl_single!(u8); impl_single!(u16); @@ -408,63 +416,3 @@ impl_single!(f64); impl_single!(usize); impl_single!(bool); - -impl Miniconf for String { - fn string_set( - &mut self, - mut topic_parts: core::iter::Peekable>, - value: &[u8], - ) -> Result<(), Error> { - if topic_parts.peek().is_some() { - return Err(Error::PathTooLong); - } - *self = serde_json_core::from_slice(value)?.0; - Ok(()) - } - - fn string_get( - &self, - mut topic_parts: core::iter::Peekable>, - value: &mut [u8], - ) -> Result { - if topic_parts.peek().is_some() { - return Err(Error::PathTooLong); - } - - serde_json_core::to_slice(self, value).map_err(|_| Error::SerializationFailed) - } - - fn get_metadata(&self) -> MiniconfMetadata { - MiniconfMetadata { - // No topic length is needed, as there are no sub-members. - max_topic_size: 0, - // One index is required for the current element. - max_depth: 1, - } - } - - // This implementation is the base case for primitives where it will - // yield once for self, then return None on subsequent calls. - fn recurse_paths( - &self, - index: &mut [usize], - _topic: &mut heapless::String, - ) -> Option<()> { - if index.is_empty() { - // Note: During expected execution paths using `iter()`, the size of the - // index stack is checked in advance to make sure this condition doesn't occur. - // However, it's possible to happen if the user manually calls `recurse_paths`. - unreachable!("Index stack too small"); - } - - let i = index[0]; - index[0] += 1; - index[1..].iter_mut().for_each(|x| *x = 0); - - if i == 0 { - Some(()) - } else { - None - } - } -} From 801f3ac89da153ec829dfc24d7b687c07f975b98 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Oct 2022 11:18:11 +0200 Subject: [PATCH 3/6] Fixing format --- src/lib.rs | 101 ++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3610bb63..88f40dc0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,7 +122,6 @@ mod option; /// Provides iteration utilities over [Miniconf] structures. pub mod iter; -use heapless::String; #[cfg(feature = "mqtt-client")] pub use mqtt_client::MqttClient; @@ -336,67 +335,67 @@ macro_rules! impl_single { }; () => { - fn string_set( - &mut self, - mut topic_parts: core::iter::Peekable>, - value: &[u8], - ) -> Result<(), Error> { - if topic_parts.peek().is_some() { - return Err(Error::PathTooLong); - } - *self = serde_json_core::from_slice(value)?.0; - Ok(()) + fn string_set( + &mut self, + mut topic_parts: core::iter::Peekable>, + value: &[u8], + ) -> Result<(), Error> { + if topic_parts.peek().is_some() { + return Err(Error::PathTooLong); + } + *self = serde_json_core::from_slice(value)?.0; + Ok(()) + } + + fn string_get( + &self, + mut topic_parts: core::iter::Peekable>, + value: &mut [u8], + ) -> Result { + if topic_parts.peek().is_some() { + return Err(Error::PathTooLong); } - fn string_get( - &self, - mut topic_parts: core::iter::Peekable>, - value: &mut [u8], - ) -> Result { - if topic_parts.peek().is_some() { - return Err(Error::PathTooLong); - } + serde_json_core::to_slice(self, value).map_err(|_| Error::SerializationFailed) + } - serde_json_core::to_slice(self, value).map_err(|_| Error::SerializationFailed) + fn get_metadata(&self) -> MiniconfMetadata { + MiniconfMetadata { + // No topic length is needed, as there are no sub-members. + max_topic_size: 0, + // One index is required for the current element. + max_depth: 1, } + } - fn get_metadata(&self) -> MiniconfMetadata { - MiniconfMetadata { - // No topic length is needed, as there are no sub-members. - max_topic_size: 0, - // One index is required for the current element. - max_depth: 1, - } + // This implementation is the base case for primitives where it will + // yield once for self, then return None on subsequent calls. + fn recurse_paths( + &self, + index: &mut [usize], + _topic: &mut heapless::String, + ) -> Option<()> { + if index.len() == 0 { + // Note: During expected execution paths using `iter()`, the size of the + // index stack is checked in advance to make sure this condition doesn't occur. + // However, it's possible to happen if the user manually calls `recurse_paths`. + unreachable!("Index stack too small"); } - // This implementation is the base case for primitives where it will - // yield once for self, then return None on subsequent calls. - fn recurse_paths( - &self, - index: &mut [usize], - _topic: &mut heapless::String, - ) -> Option<()> { - if index.len() == 0 { - // Note: During expected execution paths using `iter()`, the size of the - // index stack is checked in advance to make sure this condition doesn't occur. - // However, it's possible to happen if the user manually calls `recurse_paths`. - unreachable!("Index stack too small"); - } - - let i = index[0]; - index[0] += 1; - index[1..].iter_mut().for_each(|x| *x = 0); - - if i == 0 { - Some(()) - } else { - None - } + let i = index[0]; + index[0] += 1; + index[1..].iter_mut().for_each(|x| *x = 0); + + if i == 0 { + Some(()) + } else { + None } + } }; } -impl Miniconf for String { +impl Miniconf for heapless::String { impl_single!(); } From 6be26f7649a9d2f17841fa8352b32448ada1eea2 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Oct 2022 11:50:18 +0200 Subject: [PATCH 4/6] Adding vec support --- CHANGELOG.md | 2 +- src/array.rs | 211 +++++++++++++++++++++++++---------------------- tests/structs.rs | 19 +++++ tests/vectors.rs | 84 +++++++++++++++++++ 4 files changed, 218 insertions(+), 98 deletions(-) create mode 100644 tests/vectors.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d42add0a..ce48b638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * python module: don't emite whitespace in JSON to match serde-json-core (#92) ### Added -* `heapless::String` now implements `Miniconf` directly. +* `heapless::String` and `heapless::Vec` now implement `Miniconf` directly. ## [0.5.0] - 2022-05-12 diff --git a/src/array.rs b/src/array.rs index f522dd06..efc8118c 100644 --- a/src/array.rs +++ b/src/array.rs @@ -2,126 +2,143 @@ use super::{Error, Miniconf, MiniconfMetadata}; use core::fmt::Write; -impl Miniconf for [T; N] { - fn string_set( - &mut self, - mut topic_parts: core::iter::Peekable>, - value: &[u8], - ) -> Result<(), Error> { - let next = topic_parts.next(); - if next.is_none() { - return Err(Error::PathTooShort); - } - - // Parse what should be the index value - let i: usize = serde_json_core::from_str(next.unwrap()) - .or(Err(Error::BadIndex))? - .0; +macro_rules! impl_array { + () => { + fn string_set( + &mut self, + mut topic_parts: core::iter::Peekable>, + value: &[u8], + ) -> Result<(), Error> { + let next = topic_parts.next(); + if next.is_none() { + return Err(Error::PathTooShort); + } - if i >= self.len() { - return Err(Error::BadIndex); - } + // Parse what should be the index value + let i: usize = serde_json_core::from_str(next.unwrap()) + .or(Err(Error::BadIndex))? + .0; - self[i].string_set(topic_parts, value)?; + if i >= self.len() { + return Err(Error::BadIndex); + } - Ok(()) - } + self[i].string_set(topic_parts, value)?; - fn string_get( - &self, - mut topic_parts: core::iter::Peekable>, - value: &mut [u8], - ) -> Result { - let next = topic_parts.next(); - if next.is_none() { - return Err(Error::PathTooShort); + Ok(()) } - // Parse what should be the index value - let i: usize = serde_json_core::from_str(next.unwrap()) - .or(Err(Error::BadIndex))? - .0; - - if i >= self.len() { - return Err(Error::BadIndex); - } + fn string_get( + &self, + mut topic_parts: core::iter::Peekable>, + value: &mut [u8], + ) -> Result { + let next = topic_parts.next(); + if next.is_none() { + return Err(Error::PathTooShort); + } - self[i].string_get(topic_parts, value) - } + // Parse what should be the index value + let i: usize = serde_json_core::from_str(next.unwrap()) + .or(Err(Error::BadIndex))? + .0; - fn get_metadata(&self) -> MiniconfMetadata { - // First, figure out how many digits the maximum index requires when printing. - let mut index = N - 1; - let mut num_digits = 0; + if i >= self.len() { + return Err(Error::BadIndex); + } - while index > 0 { - index /= 10; - num_digits += 1; + self[i].string_get(topic_parts, value) } - let metadata = self[0].get_metadata(); + fn get_metadata(&self) -> MiniconfMetadata { + // First, figure out how many digits the maximum index requires when printing. + let mut index = self.len(); + let mut num_digits = 0; + + while index > 0 { + index /= 10; + num_digits += 1; + } - // If the sub-members have topic size, we also need to include an additional character for - // the path separator. This is ommitted if the sub-members have no topic (e.g. fundamental - // types, enums). - if metadata.max_topic_size > 0 { - MiniconfMetadata { - max_topic_size: metadata.max_topic_size + num_digits + 1, - max_depth: metadata.max_depth + 1, + if self.len() == 0 { + return MiniconfMetadata { + max_topic_size: num_digits, + max_depth: 1, + }; } - } else { - MiniconfMetadata { - max_topic_size: num_digits, - max_depth: metadata.max_depth + 1, + + let metadata = self[0].get_metadata(); + + // If the sub-members have topic size, we also need to include an additional character for + // the path separator. This is ommitted if the sub-members have no topic (e.g. fundamental + // types, enums). + if metadata.max_topic_size > 0 { + MiniconfMetadata { + max_topic_size: metadata.max_topic_size + num_digits + 1, + max_depth: metadata.max_depth + 1, + } + } else { + MiniconfMetadata { + max_topic_size: num_digits, + max_depth: metadata.max_depth + 1, + } } } - } - - fn recurse_paths( - &self, - index: &mut [usize], - topic: &mut heapless::String, - ) -> Option<()> { - let original_length = topic.len(); - - if index.is_empty() { - // Note: During expected execution paths using `into_iter()`, the size of the - // index stack is checked in advance to make sure this condition doesn't occur. - // However, it's possible to happen if the user manually calls `recurse_paths`. - unreachable!("Index stack too small"); - } - while index[0] < N { - // Add the array index to the topic name. - if topic.len() > 0 && topic.push('/').is_err() { - // Note: During expected execution paths using `into_iter()`, the size of the - // topic buffer is checked in advance to make sure this condition doesn't occur. - // However, it's possible to happen if the user manually calls `recurse_paths`. - unreachable!("Topic buffer too short"); - } + fn recurse_paths( + &self, + index: &mut [usize], + topic: &mut heapless::String, + ) -> Option<()> { + let original_length = topic.len(); - if write!(topic, "{}", index[0]).is_err() { + if index.is_empty() { // Note: During expected execution paths using `into_iter()`, the size of the - // topic buffer is checked in advance to make sure this condition doesn't occur. + // index stack is checked in advance to make sure this condition doesn't occur. // However, it's possible to happen if the user manually calls `recurse_paths`. - unreachable!("Topic buffer too short"); + unreachable!("Index stack too small"); } - if self[index[0]] - .recurse_paths(&mut index[1..], topic) - .is_some() - { - return Some(()); + while index[0] < self.len() { + // Add the array index to the topic name. + if topic.len() > 0 && topic.push('/').is_err() { + // Note: During expected execution paths using `into_iter()`, the size of the + // topic buffer is checked in advance to make sure this condition doesn't occur. + // However, it's possible to happen if the user manually calls `recurse_paths`. + unreachable!("Topic buffer too short"); + } + + if write!(topic, "{}", index[0]).is_err() { + // Note: During expected execution paths using `into_iter()`, the size of the + // topic buffer is checked in advance to make sure this condition doesn't occur. + // However, it's possible to happen if the user manually calls `recurse_paths`. + unreachable!("Topic buffer too short"); + } + + if self[index[0]] + .recurse_paths(&mut index[1..], topic) + .is_some() + { + return Some(()); + } + + // Strip off the previously prepended index, since we completed that element and need + // to instead check the next one. + topic.truncate(original_length); + + index[0] += 1; + index[1..].iter_mut().for_each(|x| *x = 0); } - // Strip off the previously prepended index, since we completed that element and need - // to instead check the next one. - topic.truncate(original_length); - - index[0] += 1; - index[1..].iter_mut().for_each(|x| *x = 0); + None } + }; +} + +impl Miniconf for [T; N] { + impl_array!(); +} - None - } +impl Miniconf for heapless::Vec { + impl_array!(); } diff --git a/tests/structs.rs b/tests/structs.rs index 35897a32..1798b20f 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -78,3 +78,22 @@ fn recursive_struct() { assert_eq!(metadata.max_depth, 3); assert_eq!(metadata.max_topic_size, "c/a".len()); } + +#[test] +fn struct_with_string() { + #[derive(Miniconf, Default)] + struct Settings { + string: heapless::String<10>, + } + + let mut s = Settings::default(); + + let field = "string".split('/').peekable(); + let mut buf = [0u8; 256]; + let len = s.string_get(field, &mut buf).unwrap(); + assert_eq!(&buf[..len], b"\"\""); + + let field = "string".split('/').peekable(); + s.string_set(field, br#""test""#).unwrap(); + assert_eq!(s.string, "test"); +} diff --git a/tests/vectors.rs b/tests/vectors.rs new file mode 100644 index 00000000..a335fc34 --- /dev/null +++ b/tests/vectors.rs @@ -0,0 +1,84 @@ +use miniconf::{heapless::Vec, Error, Miniconf}; + +#[test] +fn simple_vector_setting() { + let mut vec: Vec = Vec::new(); + vec.push(0).unwrap(); + + // Updating the first element should succeed. + let field = "0".split('/').peekable(); + vec.string_set(field, "7".as_bytes()).unwrap(); + assert_eq!(vec[0], 7); + + // Ensure that setting an out-of-bounds index generates an error. + let field = "1".split('/').peekable(); + assert_eq!( + vec.string_set(field, "7".as_bytes()).unwrap_err(), + Error::BadIndex + ); +} + +#[test] +fn simple_vector_getting() { + let mut vec: Vec = Vec::new(); + vec.push(7).unwrap(); + + // Get the first field + let field = "0".split('/').peekable(); + let mut buf: [u8; 256] = [0; 256]; + let len = vec.string_get(field, &mut buf).unwrap(); + assert_eq!(&buf[..len], "7".as_bytes()); + + // Ensure that getting an out-of-bounds index generates an error. + let field = "1".split('/').peekable(); + assert_eq!( + vec.string_get(field, &mut buf).unwrap_err(), + Error::BadIndex + ); + + // Pushing an item to the vec should make the second field accessible. + vec.push(2).unwrap(); + let field = "1".split('/').peekable(); + let mut buf: [u8; 256] = [0; 256]; + let len = vec.string_get(field, &mut buf).unwrap(); + assert_eq!(&buf[..len], "2".as_bytes()); +} + +#[test] +fn vector_iteration() { + let mut iterated = std::collections::HashMap::from([ + ("0".to_string(), false), + ("1".to_string(), false), + ("2".to_string(), false), + ]); + + let mut vec: Vec = Vec::new(); + vec.push(0).unwrap(); + vec.push(1).unwrap(); + vec.push(2).unwrap(); + + let mut iter_state = [0; 32]; + for field in vec.iter_settings::<256>(&mut iter_state).unwrap() { + assert!(iterated.contains_key(&field.as_str().to_string())); + iterated.insert(field.as_str().to_string(), true); + } + + // Ensure that all fields were iterated. + assert!(iterated.iter().map(|(_, value)| value).all(|&x| x)); +} + +#[test] +fn vector_metadata() { + let mut vec: Vec = Vec::new(); + + // Test metadata when the vector is empty + let metadata = vec.get_metadata(); + assert_eq!(metadata.max_depth, 1); + assert_eq!(metadata.max_topic_size, 0); + + // When the vector has items, it can be iterated across and has additional metadata. + vec.push(0).unwrap(); + let metadata = vec.get_metadata(); + assert_eq!(metadata.max_depth, 2); + assert_eq!(metadata.max_topic_size, "0".len()); +} From 50405ecbcde1c5a108a6cead4d981b0a732eb49e Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Oct 2022 15:27:17 +0200 Subject: [PATCH 5/6] Removing vec support --- src/array.rs | 211 ++++++++++++++++++++++------------------------- tests/vectors.rs | 84 ------------------- 2 files changed, 97 insertions(+), 198 deletions(-) delete mode 100644 tests/vectors.rs diff --git a/src/array.rs b/src/array.rs index efc8118c..f522dd06 100644 --- a/src/array.rs +++ b/src/array.rs @@ -2,143 +2,126 @@ use super::{Error, Miniconf, MiniconfMetadata}; use core::fmt::Write; -macro_rules! impl_array { - () => { - fn string_set( - &mut self, - mut topic_parts: core::iter::Peekable>, - value: &[u8], - ) -> Result<(), Error> { - let next = topic_parts.next(); - if next.is_none() { - return Err(Error::PathTooShort); - } +impl Miniconf for [T; N] { + fn string_set( + &mut self, + mut topic_parts: core::iter::Peekable>, + value: &[u8], + ) -> Result<(), Error> { + let next = topic_parts.next(); + if next.is_none() { + return Err(Error::PathTooShort); + } - // Parse what should be the index value - let i: usize = serde_json_core::from_str(next.unwrap()) - .or(Err(Error::BadIndex))? - .0; + // Parse what should be the index value + let i: usize = serde_json_core::from_str(next.unwrap()) + .or(Err(Error::BadIndex))? + .0; - if i >= self.len() { - return Err(Error::BadIndex); - } + if i >= self.len() { + return Err(Error::BadIndex); + } + + self[i].string_set(topic_parts, value)?; - self[i].string_set(topic_parts, value)?; + Ok(()) + } - Ok(()) + fn string_get( + &self, + mut topic_parts: core::iter::Peekable>, + value: &mut [u8], + ) -> Result { + let next = topic_parts.next(); + if next.is_none() { + return Err(Error::PathTooShort); } - fn string_get( - &self, - mut topic_parts: core::iter::Peekable>, - value: &mut [u8], - ) -> Result { - let next = topic_parts.next(); - if next.is_none() { - return Err(Error::PathTooShort); - } + // Parse what should be the index value + let i: usize = serde_json_core::from_str(next.unwrap()) + .or(Err(Error::BadIndex))? + .0; - // Parse what should be the index value - let i: usize = serde_json_core::from_str(next.unwrap()) - .or(Err(Error::BadIndex))? - .0; + if i >= self.len() { + return Err(Error::BadIndex); + } - if i >= self.len() { - return Err(Error::BadIndex); - } + self[i].string_get(topic_parts, value) + } - self[i].string_get(topic_parts, value) - } + fn get_metadata(&self) -> MiniconfMetadata { + // First, figure out how many digits the maximum index requires when printing. + let mut index = N - 1; + let mut num_digits = 0; - fn get_metadata(&self) -> MiniconfMetadata { - // First, figure out how many digits the maximum index requires when printing. - let mut index = self.len(); - let mut num_digits = 0; + while index > 0 { + index /= 10; + num_digits += 1; + } - while index > 0 { - index /= 10; - num_digits += 1; - } + let metadata = self[0].get_metadata(); - if self.len() == 0 { - return MiniconfMetadata { - max_topic_size: num_digits, - max_depth: 1, - }; + // If the sub-members have topic size, we also need to include an additional character for + // the path separator. This is ommitted if the sub-members have no topic (e.g. fundamental + // types, enums). + if metadata.max_topic_size > 0 { + MiniconfMetadata { + max_topic_size: metadata.max_topic_size + num_digits + 1, + max_depth: metadata.max_depth + 1, } - - let metadata = self[0].get_metadata(); - - // If the sub-members have topic size, we also need to include an additional character for - // the path separator. This is ommitted if the sub-members have no topic (e.g. fundamental - // types, enums). - if metadata.max_topic_size > 0 { - MiniconfMetadata { - max_topic_size: metadata.max_topic_size + num_digits + 1, - max_depth: metadata.max_depth + 1, - } - } else { - MiniconfMetadata { - max_topic_size: num_digits, - max_depth: metadata.max_depth + 1, - } + } else { + MiniconfMetadata { + max_topic_size: num_digits, + max_depth: metadata.max_depth + 1, } } + } + + fn recurse_paths( + &self, + index: &mut [usize], + topic: &mut heapless::String, + ) -> Option<()> { + let original_length = topic.len(); + + if index.is_empty() { + // Note: During expected execution paths using `into_iter()`, the size of the + // index stack is checked in advance to make sure this condition doesn't occur. + // However, it's possible to happen if the user manually calls `recurse_paths`. + unreachable!("Index stack too small"); + } - fn recurse_paths( - &self, - index: &mut [usize], - topic: &mut heapless::String, - ) -> Option<()> { - let original_length = topic.len(); + while index[0] < N { + // Add the array index to the topic name. + if topic.len() > 0 && topic.push('/').is_err() { + // Note: During expected execution paths using `into_iter()`, the size of the + // topic buffer is checked in advance to make sure this condition doesn't occur. + // However, it's possible to happen if the user manually calls `recurse_paths`. + unreachable!("Topic buffer too short"); + } - if index.is_empty() { + if write!(topic, "{}", index[0]).is_err() { // Note: During expected execution paths using `into_iter()`, the size of the - // index stack is checked in advance to make sure this condition doesn't occur. + // topic buffer is checked in advance to make sure this condition doesn't occur. // However, it's possible to happen if the user manually calls `recurse_paths`. - unreachable!("Index stack too small"); + unreachable!("Topic buffer too short"); } - while index[0] < self.len() { - // Add the array index to the topic name. - if topic.len() > 0 && topic.push('/').is_err() { - // Note: During expected execution paths using `into_iter()`, the size of the - // topic buffer is checked in advance to make sure this condition doesn't occur. - // However, it's possible to happen if the user manually calls `recurse_paths`. - unreachable!("Topic buffer too short"); - } - - if write!(topic, "{}", index[0]).is_err() { - // Note: During expected execution paths using `into_iter()`, the size of the - // topic buffer is checked in advance to make sure this condition doesn't occur. - // However, it's possible to happen if the user manually calls `recurse_paths`. - unreachable!("Topic buffer too short"); - } - - if self[index[0]] - .recurse_paths(&mut index[1..], topic) - .is_some() - { - return Some(()); - } - - // Strip off the previously prepended index, since we completed that element and need - // to instead check the next one. - topic.truncate(original_length); - - index[0] += 1; - index[1..].iter_mut().for_each(|x| *x = 0); + if self[index[0]] + .recurse_paths(&mut index[1..], topic) + .is_some() + { + return Some(()); } - None - } - }; -} + // Strip off the previously prepended index, since we completed that element and need + // to instead check the next one. + topic.truncate(original_length); -impl Miniconf for [T; N] { - impl_array!(); -} + index[0] += 1; + index[1..].iter_mut().for_each(|x| *x = 0); + } -impl Miniconf for heapless::Vec { - impl_array!(); + None + } } diff --git a/tests/vectors.rs b/tests/vectors.rs deleted file mode 100644 index a335fc34..00000000 --- a/tests/vectors.rs +++ /dev/null @@ -1,84 +0,0 @@ -use miniconf::{heapless::Vec, Error, Miniconf}; - -#[test] -fn simple_vector_setting() { - let mut vec: Vec = Vec::new(); - vec.push(0).unwrap(); - - // Updating the first element should succeed. - let field = "0".split('/').peekable(); - vec.string_set(field, "7".as_bytes()).unwrap(); - assert_eq!(vec[0], 7); - - // Ensure that setting an out-of-bounds index generates an error. - let field = "1".split('/').peekable(); - assert_eq!( - vec.string_set(field, "7".as_bytes()).unwrap_err(), - Error::BadIndex - ); -} - -#[test] -fn simple_vector_getting() { - let mut vec: Vec = Vec::new(); - vec.push(7).unwrap(); - - // Get the first field - let field = "0".split('/').peekable(); - let mut buf: [u8; 256] = [0; 256]; - let len = vec.string_get(field, &mut buf).unwrap(); - assert_eq!(&buf[..len], "7".as_bytes()); - - // Ensure that getting an out-of-bounds index generates an error. - let field = "1".split('/').peekable(); - assert_eq!( - vec.string_get(field, &mut buf).unwrap_err(), - Error::BadIndex - ); - - // Pushing an item to the vec should make the second field accessible. - vec.push(2).unwrap(); - let field = "1".split('/').peekable(); - let mut buf: [u8; 256] = [0; 256]; - let len = vec.string_get(field, &mut buf).unwrap(); - assert_eq!(&buf[..len], "2".as_bytes()); -} - -#[test] -fn vector_iteration() { - let mut iterated = std::collections::HashMap::from([ - ("0".to_string(), false), - ("1".to_string(), false), - ("2".to_string(), false), - ]); - - let mut vec: Vec = Vec::new(); - vec.push(0).unwrap(); - vec.push(1).unwrap(); - vec.push(2).unwrap(); - - let mut iter_state = [0; 32]; - for field in vec.iter_settings::<256>(&mut iter_state).unwrap() { - assert!(iterated.contains_key(&field.as_str().to_string())); - iterated.insert(field.as_str().to_string(), true); - } - - // Ensure that all fields were iterated. - assert!(iterated.iter().map(|(_, value)| value).all(|&x| x)); -} - -#[test] -fn vector_metadata() { - let mut vec: Vec = Vec::new(); - - // Test metadata when the vector is empty - let metadata = vec.get_metadata(); - assert_eq!(metadata.max_depth, 1); - assert_eq!(metadata.max_topic_size, 0); - - // When the vector has items, it can be iterated across and has additional metadata. - vec.push(0).unwrap(); - let metadata = vec.get_metadata(); - assert_eq!(metadata.max_depth, 2); - assert_eq!(metadata.max_topic_size, "0".len()); -} From d57464cbf1b66901fc6a17f449f56830248dba33 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Oct 2022 16:45:44 +0200 Subject: [PATCH 6/6] Forcing repoll on GHA --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f161b334..cb3254b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed * Python device discovery now only discovers unique device identifiers. See [#97](https://github.com/quartiq/miniconf/issues/97) + ## [0.5.0] - 2022-05-12 ### Changed