pub fn is_truthy(val: &str) -> bool {
matches!(
val.trim().to_lowercase().as_str(),
"1" | "true" | "on" | "yes"
)
}
pub fn is_falsey(val: &str) -> bool {
matches!(
val.trim().to_lowercase().as_str(),
"" | "0" | "false" | "off" | "no"
)
}
pub fn parse_bool(val: &str) -> anyhow::Result<bool> {
if is_truthy(val) {
Ok(true)
} else if is_falsey(val) {
Ok(false)
} else {
anyhow::bail!(
"Invalid boolean value: '{}'. Expected one of: true/false, 1/0, on/off, yes/no",
val
)
}
}
pub fn parse_bool_opt(val: &str) -> Option<bool> {
if is_truthy(val) {
Some(true)
} else if val.trim().is_empty() {
None
} else if is_falsey(val) {
Some(false)
} else {
None
}
}
pub fn env_is_truthy(env: &str) -> bool {
match std::env::var(env) {
Ok(val) => is_truthy(val.as_str()),
Err(_) => false,
}
}
pub fn env_is_falsey(env: &str) -> bool {
match std::env::var(env) {
Ok(val) => is_falsey(val.as_str()),
Err(_) => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
const TRUTHY: &[&str] = &[
"1", "true", "TRUE", "True", "on", "ON", "On", "yes", "YES", "Yes", " true ", "\tyes\n",
];
const FALSEY: &[&str] = &[
"0", "false", "FALSE", "False", "off", "OFF", "Off", "no", "NO", "No", "", " ", " false ",
];
const NEITHER: &[&str] = &[
"2", "enabled", "disabled", "maybe", "y", "n", "t", "f", "-1",
];
#[test]
fn truthy_spellings() {
for val in TRUTHY {
assert!(is_truthy(val), "value={val:?}");
assert!(!is_falsey(val), "value={val:?}");
assert!(parse_bool(val).unwrap(), "value={val:?}");
}
}
#[test]
fn falsey_spellings() {
for val in FALSEY {
assert!(is_falsey(val), "value={val:?}");
assert!(!is_truthy(val), "value={val:?}");
assert!(!parse_bool(val).unwrap(), "value={val:?}");
}
}
#[test]
fn invalid_spellings() {
for val in NEITHER {
assert!(!is_truthy(val), "value={val:?}");
assert!(!is_falsey(val), "value={val:?}");
assert!(parse_bool(val).is_err(), "value={val:?}");
}
}
#[test]
fn parse_bool_opt_spellings() {
for val in TRUTHY {
assert_eq!(parse_bool_opt(val), Some(true), "value={val:?}");
}
for val in FALSEY {
let expected = if val.trim().is_empty() {
None } else {
Some(false)
};
assert_eq!(parse_bool_opt(val), expected, "value={val:?}");
}
for val in NEITHER {
assert_eq!(parse_bool_opt(val), None, "value={val:?}");
}
}
#[test]
fn env_helpers() {
const UNSET: &str = "DYN_TRUTHY_TEST_UNSET";
assert!(!env_is_truthy(UNSET));
assert!(!env_is_falsey(UNSET));
const SET: &str = "DYN_TRUTHY_TEST_SET";
for (val, truthy, falsey) in [("on", true, false), ("off", false, true), ("", false, true)]
{
unsafe { std::env::set_var(SET, val) };
assert_eq!(env_is_truthy(SET), truthy, "value={val:?}");
assert_eq!(env_is_falsey(SET), falsey, "value={val:?}");
}
}
}