summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-09-09 11:42:23 -0400
committerpommicket <pommicket@gmail.com>2025-09-09 11:42:23 -0400
commitbaf62a2566aa87eed42081e0fd881e89cb721415 (patch)
tree7e74434bb0def05d9f188b105d6dc771c4c11375
parent9c72ccfe61df5cc2ef05f2982b748d73ff28b406 (diff)
Start interpretation tests
-rw-r--r--src/lib.rs40
-rw-r--r--src/tests/interpretation.rs61
-rw-r--r--src/tests/mod.rs1
3 files changed, 79 insertions, 23 deletions
diff --git a/src/lib.rs b/src/lib.rs
index acd3eb1..363b132 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -195,27 +195,17 @@ pub type Result<T> = core::result::Result<T, Error>;
fn parse_int(location: &Location, string: &str) -> Result<i64> {
let bad_int = || Error::BadInt(location.clone(), string.into());
- if !string
- .bytes()
- .all(|c| c.is_ascii_hexdigit() || c == b'x' || c == b'X' || c == b'-' || c == b'+')
- {
- return Err(bad_int());
+ let mut sign = "+";
+ let mut signless = string;
+ if let Some(s) = string.strip_prefix(['-', '+']) {
+ sign = &string[..1];
+ signless = s;
}
- let signless = string.strip_prefix(['-', '+']).unwrap_or(string);
- let mut base = 10;
- let baseless = signless
- .strip_prefix("0x")
- .or_else(|| signless.strip_prefix("0X"))
- .unwrap_or_else(|| {
- base = 16;
- signless
- });
- for digit in baseless.bytes() {
- if base == 10 && !digit.is_ascii_digit() {
- return Err(bad_int());
- }
+ if signless.starts_with('+') {
+ return Err(bad_int());
}
- string.parse().map_err(|_| bad_int())
+ let uint = parse_uint(location, signless).map_err(|_| bad_int())? as i64;
+ if sign == "-" { Ok(-uint) } else { Ok(uint) }
}
fn parse_uint(location: &Location, string: &str) -> Result<u64> {
@@ -227,20 +217,24 @@ fn parse_uint(location: &Location, string: &str) -> Result<u64> {
return Err(bad_uint());
}
let signless = string.strip_prefix('+').unwrap_or(string);
- let mut base = 10;
+ let mut base = 16;
let baseless = signless
.strip_prefix("0x")
.or_else(|| signless.strip_prefix("0X"))
.unwrap_or_else(|| {
- base = 16;
+ base = 10;
signless
});
+ if baseless.len() > 1 && baseless.starts_with('0') && base == 10 {
+ // decimal leading zeroes are not allowed
+ return Err(bad_uint());
+ }
for digit in baseless.bytes() {
if base == 10 && !digit.is_ascii_digit() {
return Err(bad_uint());
}
}
- let val = signless.parse().map_err(|_| bad_uint())?;
+ let val = u64::from_str_radix(baseless, base).map_err(|_| bad_uint())?;
if val > i64::MAX as u64 {
return Err(bad_uint());
}
@@ -648,7 +642,7 @@ impl Parser {
impl Configuration {
/// Load a configuration.
///
- /// `reader` can be `&str`, `&[u8]`, or anything that implements [`std::io::BufRead`]
+ /// `reader` can be `&[u8]` or anything that implements [`std::io::BufRead`]
/// (if the `std` feature is enabled) such as `std::io::BufReader<std::fs::File>`.
///
/// `filename` is used in error messages.
diff --git a/src/tests/interpretation.rs b/src/tests/interpretation.rs
new file mode 100644
index 0000000..64106af
--- /dev/null
+++ b/src/tests/interpretation.rs
@@ -0,0 +1,61 @@
+use crate::Configuration;
+use std::error::Error;
+use std::fmt::Display;
+
+fn do_list_test(_cfg: &Configuration) -> Result<(), Box<dyn Error>> {
+ todo!()
+}
+
+fn do_goodbad_test<I: PartialEq + Display>(
+ cfg: &Configuration,
+ name: &str,
+ parser: impl Fn(&Configuration, &str) -> Option<crate::Result<I>>,
+) -> Result<(), Box<dyn Error>> {
+ let good = cfg.section("good");
+ for key in good.keys() {
+ let [a_value, b_value] = ["a", "b"].map(|c| {
+ parser(&good, &format!("{key}.{c}"))
+ .ok_or_else(|| format!("expected to find key good.{key}.{c}"))?
+ .map_err(|e| format!("parsing good.{key}.{c} failed: {e}"))
+ });
+ let a_value = a_value?;
+ let b_value = b_value?;
+ if a_value != b_value {
+ Err(format!(
+ "{name} incorrectly parsed: good.{key}.a = {a_value}, but good.{key}.b = {b_value}"
+ ))?;
+ }
+ }
+ let bad = cfg.section("bad");
+ for (key, val) in bad.iter() {
+ if parser(&bad, key)
+ .ok_or_else(|| format!("parsing bad.{key} failed: {key} not found"))?
+ .is_ok()
+ {
+ Err(format!(
+ "bad.{key} value {val:?} should be rejected as a(n) {name} but it wasn't"
+ ))?;
+ }
+ }
+ Ok(())
+}
+
+#[test]
+fn interpretation() {
+ super::do_tests_in_dir("interpretation", ".pom", |filename| {
+ let cfg = Configuration::load_path(filename)?;
+ if filename.ends_with("list.pom") {
+ do_list_test(&cfg)
+ } else if filename.ends_with("uint.pom") {
+ do_goodbad_test(&cfg, "uint", Configuration::get_uint)
+ } else if filename.ends_with("int.pom") {
+ do_goodbad_test(&cfg, "int", Configuration::get_int)
+ } else if filename.ends_with("float.pom") {
+ do_goodbad_test(&cfg, "float", Configuration::get_float)
+ } else if filename.ends_with("bool.pom") {
+ do_goodbad_test(&cfg, "bool", Configuration::get_bool)
+ } else {
+ panic!("unrecognized test: {filename}")
+ }
+ })
+}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 644c131..6c40f6f 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -1,4 +1,5 @@
mod errors;
+mod interpretation;
mod locations;
mod parsing;