pub fn flag(key: &str) -> bool {
match std::env::var(key) {
Ok(v) => {
let v = v.trim();
!(v.is_empty()
|| v == "0"
|| v.eq_ignore_ascii_case("false")
|| v.eq_ignore_ascii_case("no")
|| v.eq_ignore_ascii_case("off"))
}
Err(_) => false,
}
}
pub fn nonempty(key: &str) -> Option<String> {
match std::env::var(key) {
Ok(v) => {
let v = v.trim();
(!v.is_empty()).then(|| v.to_string())
}
Err(_) => None,
}
}
pub fn parse<T: std::str::FromStr>(key: &str) -> Option<T> {
std::env::var(key).ok().and_then(|v| v.trim().parse().ok())
}
pub fn debug_enabled() -> bool {
static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*ON.get_or_init(|| flag("DOCLING_RS_DEBUG"))
}
#[macro_export]
macro_rules! debug_log {
($($arg:tt)*) => {
if $crate::env::debug_enabled() {
eprintln!($($arg)*);
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flag_spellings() {
for on in ["1", "true", "yes", "on", "anything", " 1 ", "TRUE"] {
std::env::set_var("DOCLING_TEST_FLAG_ON", on);
assert!(flag("DOCLING_TEST_FLAG_ON"), "{on:?} should enable");
}
for off in ["", "0", "false", "no", "off", " OFF ", "No"] {
std::env::set_var("DOCLING_TEST_FLAG_OFF", off);
assert!(!flag("DOCLING_TEST_FLAG_OFF"), "{off:?} should disable");
}
assert!(!flag("DOCLING_TEST_FLAG_UNSET"));
}
#[test]
fn nonempty_trims_and_drops_blank() {
std::env::set_var("DOCLING_TEST_NONEMPTY", " x ");
assert_eq!(nonempty("DOCLING_TEST_NONEMPTY").as_deref(), Some("x"));
std::env::set_var("DOCLING_TEST_NONEMPTY_BLANK", " ");
assert_eq!(nonempty("DOCLING_TEST_NONEMPTY_BLANK"), None);
assert_eq!(nonempty("DOCLING_TEST_NONEMPTY_UNSET"), None);
}
#[test]
fn parse_trims_and_ignores_garbage() {
std::env::set_var("DOCLING_TEST_PARSE", " 42 ");
assert_eq!(parse::<usize>("DOCLING_TEST_PARSE"), Some(42));
std::env::set_var("DOCLING_TEST_PARSE_BAD", "many");
assert_eq!(parse::<usize>("DOCLING_TEST_PARSE_BAD"), None);
assert_eq!(parse::<usize>("DOCLING_TEST_PARSE_UNSET"), None);
}
}