diff options
author | pommicket <pommicket@gmail.com> | 2025-09-10 11:57:03 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-10 11:57:03 -0400 |
commit | f1767908837d0e6a0da2aa73009ce11de6fb359d (patch) | |
tree | 8cb03ac742d3e7d2f36ccf38a319b5bc13cf4685 /src/lib.rs | |
parent | ed0182736a20e0987c6dc9c5e086a30fd1b02f8b (diff) |
Remove schemas, add unread_keys
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 122 |
1 files changed, 51 insertions, 71 deletions
@@ -13,9 +13,8 @@ use alloc::vec::Vec; use alloc::{format, vec}; use core::fmt; use core::mem::take; +use core::sync::atomic::{AtomicBool, Ordering}; -mod schema; -pub use schema::Schema; #[cfg(test)] mod tests; @@ -38,19 +37,6 @@ impl Location { pub fn line(&self) -> u64 { self.line } - - /// Dummy location for internal use - fn dummy() -> Self { - Self { - file: Arc::from(""), - line: 0, - } - } - - fn is_dummy(&self) -> bool { - // we never use line number of 0 ordinarily - self.line == 0 - } } impl fmt::Display for Location { @@ -60,17 +46,18 @@ impl fmt::Display for Location { } /// A string value, together with location information about where it is defined. -#[derive(Clone, Debug)] +#[derive(Debug)] struct Value { value: Box<str>, defined_at: Location, + read: AtomicBool, } /// A parsed POM configuration. #[derive(Clone, Debug, Default)] pub struct Configuration { /// List of items in configuration, sorted by key. - items: Vec<(Box<str>, Value)>, + items: Vec<(Box<str>, Arc<Value>)>, } impl fmt::Display for Configuration { @@ -82,7 +69,7 @@ impl fmt::Display for Configuration { } } -/// A parsing or schema error. +/// A parsing error. #[non_exhaustive] #[derive(Debug)] pub enum Error { @@ -134,18 +121,6 @@ pub enum Error { /// None of the errors in the array will be [`Error::Multiple`]'s, /// and the array will contain at least two elements. Multiple(Box<[Error]>), - /// Bad type in schema - SchemaBadType(Location, Box<str>), - /// Bad maxlength in schema - SchemaBadMaxLength(Location, Box<str>), - /// Invalid schema key - SchemaBadKey(Location, Box<str>), - /// Value is less than the schema-imposed minimum - SchemaValueLessThanMin(Location, Box<str>, f64, f64), - /// Value is greater than the schema-imposed maximum - SchemaValueGreaterThanMax(Location, Box<str>, f64, f64), - /// Value is greater than the schema-imposed maxlength - SchemaValueTooLong(Location, Box<str>, usize, usize), } impl fmt::Display for Error { @@ -204,21 +179,6 @@ impl fmt::Display for Error { } Ok(()) } - Self::SchemaBadType(l, t) => write!(f, "{l}: invalid type: {t:?}"), - Self::SchemaBadMaxLength(l, m) => write!(f, "{l}: invalid maxlength: {m:?}"), - Self::SchemaBadKey(l, k) => write!(f, "{l}: invalid schema key: {k}"), - Self::SchemaValueLessThanMin(l, key, val, min) => write!( - f, - "{l}: {key}'s value of {val} is less than the minimum ({min})" - ), - Self::SchemaValueGreaterThanMax(l, key, val, max) => write!( - f, - "{l}: {key}'s value of {val} is greater than the maximum ({max})" - ), - Self::SchemaValueTooLong(l, key, len, maxlen) => write!( - f, - "{l}: {key}'s value has length {len}, which exceeds the maximum of {maxlen}" - ), } } } @@ -621,7 +581,7 @@ impl Parser { } fn load(&mut self, filename: &str, reader: &mut dyn Read) -> Result<Configuration> { - let mut items: Vec<(Box<str>, Value)> = vec![]; + let mut items: Vec<(Box<str>, Arc<Value>)> = vec![]; let mut line: Vec<u8> = vec![]; let mut line_number: u64 = 0; let mut current_section = String::new(); @@ -673,10 +633,11 @@ impl Parser { self.read_quoted_value(value, reader, &location)?; items.push(( key.into(), - Value { + Arc::new(Value { value: value.into(), defined_at: location, - }, + read: AtomicBool::new(false), + }), )); line_number = new_line_number; } else { @@ -686,10 +647,11 @@ impl Parser { } items.push(( key.into(), - Value { + Arc::new(Value { value: value.into(), defined_at: location, - }, + read: AtomicBool::new(false), + }), )); } } @@ -801,29 +763,25 @@ impl Configuration { self.into_iter() } - /// Same as `iter()` (for now), but explicitly marks that keys should be in sorted order. - /// - /// This is used internally so that we know what we have to fix if `iter()` - /// is ever changed to return a non-sorted iterator. - fn iter_sorted(&self) -> ConfigurationIter<'_> { - self.iter() - } - - fn get_val(&self, key: &str) -> Option<&Value> { + fn get_val(&self, key: &str, mark_read: bool) -> Option<&Value> { let idx = self.binary_search_for(key).ok()?; - Some(&self.items[idx].1) + let v = &self.items[idx].1; + if mark_read { + v.read.store(true, Ordering::Relaxed); + } + Some(v) } /// Get value associated with `key`, if any. #[must_use] pub fn get(&self, key: &str) -> Option<&str> { - Some(self.get_val(key)?.value.as_ref()) + Some(self.get_val(key, true)?.value.as_ref()) } /// Get location in the configuration file where `key` is defined, if any. #[must_use] pub fn location(&self, key: &str) -> Option<Location> { - if let Some(val) = self.get_val(key) { + if let Some(val) = self.get_val(key, false) { Some(val.defined_at.clone()) } else { // Check if `key` has any defined subkeys @@ -855,7 +813,9 @@ impl Configuration { /// and `Some(Err(…))` if `key` is defined but not an integer. #[must_use] pub fn get_int(&self, key: &str) -> Option<Result<i64>> { - let Value { value, defined_at } = self.get_val(key)?; + let Value { + value, defined_at, .. + } = self.get_val(key, true)?; Some(parse_int(defined_at, value.as_ref())) } @@ -872,7 +832,9 @@ impl Configuration { /// and `Some(Err(…))` if `key` is defined but not an unsigned integer. #[must_use] pub fn get_uint(&self, key: &str) -> Option<Result<u64>> { - let Value { value, defined_at } = self.get_val(key)?; + let Value { + value, defined_at, .. + } = self.get_val(key, true)?; Some(parse_uint(defined_at, value.as_ref())) } @@ -889,7 +851,9 @@ impl Configuration { /// and `Some(Err(…))` if `key` is defined but not a float. #[must_use] pub fn get_float(&self, key: &str) -> Option<Result<f64>> { - let Value { value, defined_at } = self.get_val(key)?; + let Value { + value, defined_at, .. + } = self.get_val(key, true)?; Some(parse_float(defined_at, value.as_ref())) } @@ -907,7 +871,9 @@ impl Configuration { /// `off`, `no`, `false`, `on`, `yes`, `true`. #[must_use] pub fn get_bool(&self, key: &str) -> Option<Result<bool>> { - let Value { value, defined_at } = self.get_val(key)?; + let Value { + value, defined_at, .. + } = self.get_val(key, true)?; Some(parse_bool(defined_at, value.as_ref())) } @@ -924,7 +890,7 @@ impl Configuration { /// Commas in list entries can be escaped with `\,`. #[must_use] pub fn get_list(&self, key: &str) -> Option<Vec<String>> { - let value = &self.get_val(key)?.value; + let value = &self.get_val(key, true)?.value; Some(parse_list(value.as_ref())) } @@ -967,15 +933,29 @@ impl Configuration { } } - /// Parse `self` as a [`Schema`]. - pub fn to_schema(&self) -> Result<Schema> { - Schema::try_from(self) + /// Returns an iterator over all keys whose values have not been read. + /// + /// This includes getting them through [`Self::get`], [`Self::get_or_default`], [`Self::get_int`], etc. + /// It also includes getting them through [`Self::get`] called on a section obtained via [`Self::section`]. + /// + /// The order of the items returned is arbitrary and may change in future versions without notice. + /// + /// Beware of race conditions when using this function in a multithreaded program + /// (you should wait for all threads to finish reading the configuration before calling this). + pub fn unread_keys(&self) -> impl '_ + Iterator<Item = &str> { + self.items.iter().filter_map(|(k, v)| { + if !v.read.load(Ordering::Relaxed) { + Some(k.as_ref()) + } else { + None + } + }) } } /// Opaque type returned by [`Configuration::iter`]. #[derive(Clone, Debug)] -pub struct ConfigurationIter<'a>(core::slice::Iter<'a, (Box<str>, Value)>); +pub struct ConfigurationIter<'a>(core::slice::Iter<'a, (Box<str>, Arc<Value>)>); impl<'a> Iterator for ConfigurationIter<'a> { type Item = (&'a str, &'a str); |