use std::fmt::Debug;
use std::sync::OnceLock;
pub(crate) struct Knob<T: 'static> {
name: &'static str,
log_target: &'static str,
parse: fn(&str) -> Option<T>,
default: fn() -> T,
value: OnceLock<T>,
}
impl<T: Debug + Send + Sync> Knob<T> {
pub(crate) const fn new(
name: &'static str,
log_target: &'static str,
parse: fn(&str) -> Option<T>,
default: fn() -> T,
) -> Self {
Self { name, log_target, parse, default, value: OnceLock::new() }
}
pub(crate) fn get(&self) -> &T {
self.value.get_or_init(|| self.resolve(std::env::var(self.name).ok().as_deref()))
}
pub(crate) fn resolve(&self, raw: Option<&str>) -> T {
let value = match raw.map(str::trim).filter(|text| !text.is_empty()) {
None => (self.default)(),
Some(text) => (self.parse)(text).unwrap_or_else(|| {
log::warn!(
target: self.log_target,
"{}={text:?} is not a valid value; using the default",
self.name
);
(self.default)()
}),
};
log::debug!(target: self.log_target, "{} = {value:?}", self.name);
value
}
}
pub(crate) fn parse_positive(text: &str) -> Option<usize> {
text.parse::<usize>().ok().filter(|&n| n > 0)
}
#[cfg(test)]
mod tests;