Skip to main content

rusty_pee/
mode.rs

1//! Compatibility mode resolution.
2//!
3//! Precedence ladder (FR-012):
4//! 1. Explicit `--strict` / `--no-strict` flag wins over everything.
5//! 2. `RUSTY_PEE_STRICT=1` env var (any truthy value).
6//! 3. `argv[0]` basename equals `pee` (after `.exe` strip on Windows).
7//! 4. Default mode.
8
9use crate::CompatibilityMode;
10use std::ffi::OsStr;
11use std::path::Path;
12
13/// Resolve the compatibility mode from CLI flag, env var, and argv[0].
14pub fn resolve(
15    strict_flag: Option<bool>,
16    env_strict: Option<&OsStr>,
17    argv0: Option<&OsStr>,
18) -> CompatibilityMode {
19    if let Some(flag) = strict_flag {
20        return if flag {
21            CompatibilityMode::Strict
22        } else {
23            CompatibilityMode::Default
24        };
25    }
26    if let Some(value) = env_strict {
27        if env_var_is_truthy(value) {
28            return CompatibilityMode::Strict;
29        }
30    }
31    if let Some(arg0) = argv0 {
32        if argv0_implies_strict(arg0) {
33            return CompatibilityMode::Strict;
34        }
35    }
36    CompatibilityMode::Default
37}
38
39fn env_var_is_truthy(value: &OsStr) -> bool {
40    let Some(s) = value.to_str() else {
41        return false;
42    };
43    matches!(
44        s.trim().to_ascii_lowercase().as_str(),
45        "1" | "true" | "yes" | "on"
46    )
47}
48
49fn argv0_implies_strict(arg0: &OsStr) -> bool {
50    let Some(stem) = Path::new(arg0).file_stem() else {
51        return false;
52    };
53    stem == OsStr::new("pee")
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn explicit_strict_flag_wins() {
62        assert_eq!(resolve(Some(true), None, None), CompatibilityMode::Strict);
63        assert_eq!(
64            resolve(Some(false), Some(OsStr::new("1")), Some(OsStr::new("pee"))),
65            CompatibilityMode::Default,
66            "explicit --no-strict beats env and argv[0]"
67        );
68    }
69
70    #[test]
71    fn env_var_truthy_implies_strict() {
72        for v in ["1", "true", "yes", "on", "TRUE", " 1 ", "On"] {
73            assert_eq!(
74                resolve(None, Some(OsStr::new(v)), None),
75                CompatibilityMode::Strict,
76                "env value {v:?} should imply strict"
77            );
78        }
79    }
80
81    #[test]
82    fn env_var_falsy_does_not_imply_strict() {
83        for v in ["0", "false", "no", "off", ""] {
84            assert_eq!(
85                resolve(None, Some(OsStr::new(v)), None),
86                CompatibilityMode::Default,
87                "env value {v:?} should NOT imply strict"
88            );
89        }
90    }
91
92    #[test]
93    fn argv0_pee_implies_strict() {
94        assert_eq!(
95            resolve(None, None, Some(OsStr::new("pee"))),
96            CompatibilityMode::Strict
97        );
98        assert_eq!(
99            resolve(None, None, Some(OsStr::new("/usr/local/bin/pee"))),
100            CompatibilityMode::Strict
101        );
102        assert_eq!(
103            resolve(None, None, Some(OsStr::new("pee.exe"))),
104            CompatibilityMode::Strict,
105            "argv[0] = pee.exe must imply strict (file_stem strips .exe)"
106        );
107    }
108
109    #[test]
110    fn argv0_rusty_pee_does_not_imply_strict() {
111        assert_eq!(
112            resolve(None, None, Some(OsStr::new("rusty-pee"))),
113            CompatibilityMode::Default
114        );
115        assert_eq!(
116            resolve(None, None, Some(OsStr::new("rusty-pee.exe"))),
117            CompatibilityMode::Default
118        );
119    }
120
121    #[test]
122    fn default_when_nothing_set() {
123        assert_eq!(resolve(None, None, None), CompatibilityMode::Default);
124    }
125
126    #[test]
127    fn ladder_strict_flag_beats_env_var() {
128        assert_eq!(
129            resolve(Some(false), Some(OsStr::new("1")), None),
130            CompatibilityMode::Default
131        );
132        assert_eq!(
133            resolve(Some(true), Some(OsStr::new("0")), None),
134            CompatibilityMode::Strict
135        );
136    }
137
138    #[test]
139    fn ladder_env_var_beats_argv0() {
140        assert_eq!(
141            resolve(None, Some(OsStr::new("1")), Some(OsStr::new("rusty-pee"))),
142            CompatibilityMode::Strict
143        );
144        assert_eq!(
145            resolve(None, Some(OsStr::new("0")), Some(OsStr::new("pee"))),
146            CompatibilityMode::Strict,
147            "rung 2 falsy is no-op; rung 3 (argv0=pee) still engages Strict"
148        );
149    }
150}