uconsole_sleep/
args.rs

1use std::path::PathBuf;
2
3/// Parse CLI args for a minimal set: --dry-run, --toggle-wifi, --config <path>
4fn parse_cli_args_from<I: IntoIterator<Item = String>>(
5    args: I,
6) -> (bool, u8, Option<bool>, Option<PathBuf>) {
7    let mut dry_run = false;
8    let mut verbosity: u8 = 0;
9    let mut config_path: Option<PathBuf> = None;
10    let mut toggle_wifi: Option<bool> = None;
11    let mut iter = args.into_iter();
12    while let Some(a) = iter.next() {
13        match a.as_str() {
14            "--dry-run" => dry_run = true,
15            s if s.starts_with("-v") && s.chars().skip(1).all(|c| c == 'v') => {
16                let count = s.chars().skip(1).count();
17                // bound verbosity at 3
18                verbosity = std::cmp::min(3, count as u8);
19            }
20            "--verbose" => {
21                // map --verbose to single -v
22                verbosity = std::cmp::max(verbosity, 1);
23            }
24            s if s.starts_with("--toggle-wifi") => {
25                if s == "--toggle-wifi" {
26                    toggle_wifi = Some(true);
27                } else if let Some(eq) = s.find('=') {
28                    let val = s[eq + 1..].to_ascii_lowercase();
29                    toggle_wifi = Some(val == "true" || val == "1" || val == "yes");
30                }
31            }
32            s if s.starts_with("--config") => {
33                if s == "--config" {
34                    if let Some(p) = iter.next() {
35                        config_path = Some(PathBuf::from(p));
36                    }
37                } else if let Some(eq) = s.find('=') {
38                    let p = &s[eq + 1..];
39                    if !p.is_empty() {
40                        config_path = Some(PathBuf::from(p));
41                    }
42                }
43            }
44            _ => {}
45        }
46    }
47    (dry_run, verbosity, toggle_wifi, config_path)
48}
49
50pub fn parse_cli_args() -> (bool, u8, Option<bool>, Option<PathBuf>) {
51    parse_cli_args_from(std::env::args())
52}
53
54#[cfg(test)]
55mod tests {
56    use crate::Config;
57
58    use super::*;
59
60    #[test]
61    fn test_parse_cli_args_from_flags() {
62        let tmp = std::env::temp_dir().join(format!(
63            "uconsole_cli_cfg_{}",
64            std::time::SystemTime::now()
65                .duration_since(std::time::UNIX_EPOCH)
66                .unwrap()
67                .as_millis()
68        ));
69        let _ = std::fs::create_dir_all(&tmp);
70        let cfg_path = tmp.join("cli_cfg");
71        std::fs::write(&cfg_path, "SAVING_CPU_FREQ=55,66\nHOLD_TRIGGER_SEC=1.4\n").unwrap();
72
73        let args = vec![
74            String::from("prog"),
75            String::from("--dry-run"),
76            String::from("--config"),
77            cfg_path.to_string_lossy().to_string(),
78        ];
79        let (dry_run, verbosity, _toggle_wifi, cli_config_path) = parse_cli_args_from(args);
80        assert!(dry_run);
81        assert_eq!(verbosity, 0);
82        assert_eq!(cli_config_path, Some(cfg_path.clone()));
83
84        // ensure the Config::load uses this file when provided
85        let loaded = Config::load(cli_config_path.clone());
86        assert_eq!(loaded.saving_cpu_freq.unwrap(), "55,66");
87        assert_eq!(loaded.hold_trigger_sec.unwrap(), 1.4_f32);
88        // no-op; this used to check default examples
89    }
90
91    #[test]
92    fn test_parse_cli_args_from_flags_eq_form() {
93        let tmp = std::env::temp_dir().join(format!(
94            "uconsole_cli_cfg_{}",
95            std::time::SystemTime::now()
96                .duration_since(std::time::UNIX_EPOCH)
97                .unwrap()
98                .as_millis()
99        ));
100        let _ = std::fs::create_dir_all(&tmp);
101        let cfg_path = tmp.join("cli_cfg2");
102        std::fs::write(&cfg_path, "SAVING_CPU_FREQ=22,33\nHOLD_TRIGGER_SEC=2.1\n").unwrap();
103
104        let args = vec![
105            String::from("prog"),
106            String::from("--dry-run"),
107            format!("--config={}", cfg_path.to_string_lossy()),
108        ];
109        let (dry_run, verbosity, _toggle_wifi, cli_config_path) = parse_cli_args_from(args);
110        assert!(dry_run);
111        assert_eq!(verbosity, 0);
112        assert_eq!(cli_config_path, Some(cfg_path.clone()));
113        let loaded = Config::load(cli_config_path.clone());
114        assert_eq!(loaded.saving_cpu_freq.unwrap(), "22,33");
115        assert_eq!(loaded.hold_trigger_sec.unwrap(), 2.1_f32);
116    }
117
118    #[test]
119    fn test_toggle_wifi_cli_precedence_over_config() {
120        use crate::Config;
121        let cfg = Config {
122            toggle_wifi: true,
123            ..Default::default()
124        };
125        let toggle_wifi_flag = Some(false);
126        let final_toggle_wifi = match toggle_wifi_flag {
127            Some(v) => v,
128            None => cfg.toggle_wifi,
129        };
130        assert!(!final_toggle_wifi);
131    }
132
133    #[test]
134    fn test_parse_cli_args_verbosity_v() {
135        let args = vec![String::from("prog"), String::from("-v")];
136        let (_dry_run, verbosity, _toggle_wifi, _cli_config_path) = parse_cli_args_from(args);
137        assert_eq!(verbosity, 1);
138    }
139
140    #[test]
141    fn test_parse_cli_args_verbosity_vv() {
142        let args = vec![String::from("prog"), String::from("-vv")];
143        let (_dry_run, verbosity, _toggle_wifi, _cli_config_path) = parse_cli_args_from(args);
144        assert_eq!(verbosity, 2);
145    }
146
147    #[test]
148    fn test_parse_cli_args_verbosity_vvv() {
149        let args = vec![String::from("prog"), String::from("-vvv")];
150        let (_dry_run, verbosity, _toggle_wifi, _cli_config_path) = parse_cli_args_from(args);
151        assert_eq!(verbosity, 3);
152    }
153}