Skip to main content

ralph_workflow/config/
parser.rs

1//! Environment variable parsing helpers.
2//!
3//! The unified config loader (`crate::config::loader`) owns the full
4//! configuration-loading flow; this module keeps only shared helpers.
5
6/// Parse a boolean from an environment variable value.
7///
8/// Accepts common truthy and falsy values:
9/// - Truthy: "1", "true", "yes", "y", "on"
10/// - Falsy: "0", "false", "no", "n", "off"
11///
12/// # Arguments
13///
14/// * `value` - The string value to parse
15///
16/// # Returns
17///
18/// Returns `Some(true)` for truthy values, `Some(false)` for falsy values,
19/// and `None` for empty or unrecognized values.
20#[must_use]
21pub fn parse_env_bool(value: &str) -> Option<bool> {
22    let normalized = value.trim().to_ascii_lowercase();
23    match normalized.as_str() {
24        "1" | "true" | "yes" | "y" | "on" => Some(true),
25        "0" | "false" | "no" | "n" | "off" => Some(false),
26        _ => None,
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn test_parse_env_bool() {
36        assert_eq!(parse_env_bool("1"), Some(true));
37        assert_eq!(parse_env_bool("true"), Some(true));
38        assert_eq!(parse_env_bool(" TRUE "), Some(true));
39        assert_eq!(parse_env_bool("on"), Some(true));
40        assert_eq!(parse_env_bool("yes"), Some(true));
41
42        assert_eq!(parse_env_bool("0"), Some(false));
43        assert_eq!(parse_env_bool("false"), Some(false));
44        assert_eq!(parse_env_bool(" FALSE "), Some(false));
45        assert_eq!(parse_env_bool("off"), Some(false));
46        assert_eq!(parse_env_bool("no"), Some(false));
47
48        assert_eq!(parse_env_bool(""), None);
49        assert_eq!(parse_env_bool("maybe"), None);
50    }
51}
52
53#[cfg(test)]
54mod proptest_parsers {
55    use super::parse_env_bool;
56    use proptest::prelude::*;
57
58    proptest! {
59        /// `parse_env_bool` must never panic on any string input.
60        #[test]
61        fn parse_env_bool_never_panics(s in ".*") {
62            let _ = parse_env_bool(&s);
63        }
64
65        /// Known truthy strings always return `Some(true)` regardless of surrounding whitespace.
66        #[test]
67        fn parse_env_bool_truthy_values(
68            val in prop_oneof!["1", "true", "yes", "y", "on",
69                               "TRUE", "YES", "ON", "True", "Yes"],
70            prefix in "[ \t]*",
71            suffix in "[ \t]*",
72        ) {
73            let padded = format!("{prefix}{val}{suffix}");
74            prop_assert_eq!(parse_env_bool(&padded), Some(true));
75        }
76
77        /// Known falsy strings always return `Some(false)` regardless of surrounding whitespace.
78        #[test]
79        fn parse_env_bool_falsy_values(
80            val in prop_oneof!["0", "false", "no", "n", "off",
81                               "FALSE", "NO", "OFF", "False", "No"],
82            prefix in "[ \t]*",
83            suffix in "[ \t]*",
84        ) {
85            let padded = format!("{prefix}{val}{suffix}");
86            prop_assert_eq!(parse_env_bool(&padded), Some(false));
87        }
88
89        /// Result is always `Some(true)`, `Some(false)`, or `None` — never anything else.
90        #[test]
91        fn parse_env_bool_result_is_valid(s in ".*") {
92            let result = parse_env_bool(&s);
93            prop_assert!(result == Some(true) || result == Some(false) || result.is_none());
94        }
95    }
96}