summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-09-08 22:01:30 -0400
committerpommicket <pommicket@gmail.com>2025-09-08 22:01:30 -0400
commite3a4ef53a75bc10129010cf18fb7ce68a6acbf2a (patch)
tree6d7aeabe48ca02f0f83ac6a59178f9b7ca6aadac /src
parentaf57af207e5106a47e50ae1ca6de7f746cfe8da6 (diff)
Add iter(), more tests
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs32
-rw-r--r--src/tests/locations.rs24
-rw-r--r--src/tests/mod.rs72
-rw-r--r--src/tests/parsing.rs63
4 files changed, 117 insertions, 74 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 4a12e5b..56ecb61 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -689,7 +689,7 @@ impl Configuration {
}
}
- /// Get the list of all “direct keys” in this configuration.
+ /// Get all “direct keys” in this configuration.
///
/// More specifically, this returns an iterator of all unique
/// first components of keys in `self`.
@@ -712,6 +712,15 @@ impl Configuration {
})
}
+ /// Get all defined keys in this configuration, including nested ones,
+ /// and their values.
+ ///
+ /// The order of items returned is arbitrary and may change
+ /// in future versions without notice.
+ pub fn iter(&self) -> ConfigurationIter {
+ self.into_iter()
+ }
+
/// Get the list of all “direct children” of `key` in this configuration.
///
/// More specifically, this returns an iterator of all unique
@@ -893,3 +902,24 @@ impl Configuration {
todo!()
}
}
+
+/// Opaque type returned by [`<&Configuration>::into_iter`].
+#[derive(Clone, Debug)]
+pub struct ConfigurationIter<'a>(core::slice::Iter<'a, (Box<str>, Value)>);
+
+impl<'a> Iterator for ConfigurationIter<'a> {
+ type Item = (&'a str, &'a str);
+ fn next(&mut self) -> Option<Self::Item> {
+ let (key, val) = self.0.next()?;
+ Some((key, val.value.as_ref()))
+ }
+}
+
+impl<'a> IntoIterator for &'a Configuration {
+ type IntoIter = ConfigurationIter<'a>;
+ type Item = (&'a str, &'a str);
+ /// See [`Configuration::iter`].
+ fn into_iter(self) -> Self::IntoIter {
+ ConfigurationIter(self.items.iter())
+ }
+}
diff --git a/src/tests/locations.rs b/src/tests/locations.rs
new file mode 100644
index 0000000..2310101
--- /dev/null
+++ b/src/tests/locations.rs
@@ -0,0 +1,24 @@
+use crate::Configuration;
+
+#[test]
+fn parsing() {
+ super::do_tests_in_dir("location", ".locations.pom", |filename| {
+ let locations = Configuration::load_path(filename)?;
+ let config = Configuration::load_path(&filename.replace(".locations.pom", ".pom"))?;
+ for (key, val) in &locations {
+ let location = config.location(key).ok_or_else(|| {
+ format!("key {key} not found in main config but it exists in the location's config")
+ })?;
+ let got = location.line();
+ let expected = val
+ .parse()
+ .map_err(|_| format!("key {key} in location POM should be an integer"))?;
+ if got != expected {
+ Err(format!(
+ "key {key} reported as being at line {got}, but it should be at line {expected}"
+ ))?;
+ }
+ }
+ Ok(())
+ })
+}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 58cd2b0..83873dc 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -1,56 +1,32 @@
-use crate::Configuration;
-use std::collections::HashSet;
-
+mod locations;
mod parsing;
-fn check_configs_equal(
- cfg1: &Configuration,
- cfg2: &Configuration,
-) -> Result<(), Box<dyn std::error::Error>> {
- fn check_configs_equal_at(
- cfg1: &Configuration,
- cfg2: &Configuration,
- prefix: &str,
- ) -> Result<(), Box<dyn std::error::Error>> {
- let (keys1, keys2): (HashSet<&str>, HashSet<&str>) =
- if let Some(k) = prefix.strip_suffix('.') {
- (cfg1.subkeys(k).collect(), cfg2.subkeys(k).collect())
- } else {
- (cfg1.keys().collect(), cfg2.keys().collect())
- };
- for key in keys1.difference(&keys2) {
- return Err(format!(
- "Key {prefix}{key} appears at {}, but not in other configuration",
- cfg1.location(&format!("{prefix}{key}")).unwrap()
- )
- .into());
- }
- for key in keys2.difference(&keys1) {
- return Err(format!(
- "Key {prefix}{key} appears at {}, but not in other configuration",
- cfg2.location(&format!("{prefix}{key}")).unwrap()
+fn do_tests_in_dir(
+ dir: &str,
+ ext: &str,
+ test: impl Fn(&str) -> Result<(), Box<dyn std::error::Error>>,
+) {
+ let result = (|| -> Result<(), Box<dyn std::error::Error>> {
+ let test_dir = format!("../tests/{dir}");
+ let tests = std::fs::read_dir(&test_dir).map_err(|e| {
+ format!(
+ "error reading {test_dir}: {e} — try moving pom-rs to inside the main pom repository?"
)
- .into());
- }
- for key in &keys1 {
- let full_key = format!("{prefix}{key}");
- let val1 = cfg1.get(&full_key);
- let val2 = cfg2.get(&full_key);
- if val1 != val2 {
- return Err(format!(
- "Mismatch between values for key {full_key}
- defined as {val1:?} at {},
-but defined as {val2:?} at {}",
- cfg1.location(&full_key)
- .map_or("<nowhere>".into(), |x| format!("{x}")),
- cfg2.location(&full_key)
- .map_or("<nowhere>".into(), |x| format!("{x}")),
- )
- .into());
+ })?;
+ for test_entry in tests {
+ let filename = test_entry
+ .map_err(|e| format!("error reading tests directory {test_dir}: {e}"))?
+ .file_name()
+ .into_string()
+ .expect("bad UTF-8 in test name (file in {test_dir})");
+ if filename.ends_with(ext) {
+ println!("Running test {dir}/{filename}...");
+ test(&format!("{test_dir}/{filename}"))?
}
- check_configs_equal_at(cfg1, cfg2, &format!("{full_key}."))?;
}
Ok(())
+ })();
+ if let Err(e) = result {
+ panic!("Error: {e}");
}
- check_configs_equal_at(cfg1, cfg2, "")
}
diff --git a/src/tests/parsing.rs b/src/tests/parsing.rs
index eb98a04..97cf711 100644
--- a/src/tests/parsing.rs
+++ b/src/tests/parsing.rs
@@ -1,28 +1,39 @@
use crate::Configuration;
+use std::collections::HashSet;
-static TEST_DIR: &str = "../tests/parsing";
-
-fn single_test(flat_name: &str, complex_name: &str) -> Result<(), Box<dyn std::error::Error>> {
- println!("Running test {complex_name}...");
- let config1 = Configuration::load_path(format!("{TEST_DIR}/{flat_name}"))?;
- let config2 = Configuration::load_path(format!("{TEST_DIR}/{complex_name}"))?;
- super::check_configs_equal(&config1, &config2)
-}
-
-fn try_parsing() -> Result<(), Box<dyn std::error::Error>> {
- let tests = std::fs::read_dir(TEST_DIR).map_err(|e| {
- format!(
- "error reading {TEST_DIR}: {e} — try moving pom-rs to inside the main pom repository?"
+fn check_configs_equal(
+ cfg1: &Configuration,
+ cfg2: &Configuration,
+) -> Result<(), Box<dyn std::error::Error>> {
+ let keys1: HashSet<&str> = cfg1.iter().map(|(k, _)| k).collect();
+ let keys2: HashSet<&str> = cfg2.iter().map(|(k, _)| k).collect();
+ for key in keys1.difference(&keys2) {
+ return Err(format!(
+ "Key {key} appears at {}, but not in other configuration",
+ cfg1.location(&key).unwrap()
)
- })?;
- for test_entry in tests {
- let filename = test_entry
- .map_err(|e| format!("error reading tests directory {TEST_DIR}: {e}"))?
- .file_name()
- .into_string()
- .expect("bad UTF-8 in test name (file in {TEST_DIR})");
- if filename.ends_with(".flat.pom") {
- single_test(&filename, filename.replace(".flat.pom", ".pom").as_ref())?;
+ .into());
+ }
+ for key in keys2.difference(&keys1) {
+ return Err(format!(
+ "Key {key} appears at {}, but not in other configuration",
+ cfg2.location(&key).unwrap()
+ )
+ .into());
+ }
+ for (key, val1) in cfg1.iter() {
+ let val2 = cfg2.get(&key).unwrap();
+ if val1 != val2 {
+ return Err(format!(
+ "Mismatch between values for key {key}
+defined as {val1:?} at {},
+but defined as {val2:?} at {}",
+ cfg1.location(&key)
+ .map_or("<nowhere>".into(), |x| format!("{x}")),
+ cfg2.location(&key)
+ .map_or("<nowhere>".into(), |x| format!("{x}")),
+ )
+ .into());
}
}
Ok(())
@@ -30,7 +41,9 @@ fn try_parsing() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn parsing() {
- if let Err(e) = try_parsing() {
- panic!("Error: {e}");
- }
+ super::do_tests_in_dir("parsing", ".flat.pom", |filename| {
+ let config1 = Configuration::load_path(filename)?;
+ let config2 = Configuration::load_path(&filename.replace(".flat.pom", ".pom"))?;
+ check_configs_equal(&config1, &config2)
+ })
}