Skip to main content

miniextendr_api/
env_flag.rs

1//! Shared truthiness parsing for environment-variable flags.
2//!
3//! Two flags in this crate read boolean-ish env vars — `MINIEXTENDR_BACKTRACE`
4//! (`backtrace.rs`) and `_R_CHECK_LIMIT_CORES_` (`optionals/parallel.rs`) — and
5//! they used to disagree on what counts as "on" (`true`/`1` vs. anything but
6//! `false`). `parse_bool` is the single source of truth, deliberately aligned
7//! with R's own `config_val_to_logical` (`tools/R/utils.R`): a case-insensitive,
8//! whitespace-trimmed match against explicit truthy/falsy sets.
9//!
10//! Recognized values (case-insensitive, surrounding whitespace trimmed):
11//! - truthy: `yes`, `true`, `1`, `on`
12//! - falsy: `no`, `false`, `0`, `off`, `` (empty)
13//!
14//! Anything else is unrecognized (`None`) — the caller supplies the default,
15//! so each flag keeps control of its own "garbage" policy (see the two call
16//! sites for their deliberately different defaults).
17
18/// Parse an environment-flag value as a boolean, using R's
19/// `config_val_to_logical` rules (see module docs). Returns `None` for values
20/// outside the recognized truthy/falsy sets so the caller picks the default.
21pub(crate) fn parse_bool(v: &str) -> Option<bool> {
22    match v.trim().to_ascii_lowercase().as_str() {
23        "yes" | "true" | "1" | "on" => Some(true),
24        "no" | "false" | "0" | "off" | "" => Some(false),
25        _ => None,
26    }
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn recognizes_truthy_values_case_insensitively() {
35        for v in [
36            "yes", "YES", "true", "TRUE", "True", "1", "on", "ON", " true ",
37        ] {
38            assert_eq!(parse_bool(v), Some(true), "{v:?}");
39        }
40    }
41
42    #[test]
43    fn recognizes_falsy_values_case_insensitively() {
44        for v in ["no", "NO", "false", "FALSE", "0", "off", "OFF", "", "  "] {
45            assert_eq!(parse_bool(v), Some(false), "{v:?}");
46        }
47    }
48
49    #[test]
50    fn returns_none_for_unrecognized() {
51        for v in ["garbage", "2", "tru", "yesno"] {
52            assert_eq!(parse_bool(v), None, "{v:?}");
53        }
54    }
55}