1use std::collections::HashMap;
8use std::fs;
9use std::path::PathBuf;
10
11use crate::wifi;
12use log::Level;
13
14#[derive(Clone, Debug, Default)]
15pub struct Config {
16 pub dry_run: bool,
17 pub policy_path: Option<PathBuf>,
18 pub saving_cpu_freq: Option<String>,
19 pub hold_trigger_sec: Option<f32>,
20 pub toggle_wifi: bool,
21 pub wifi_rfkill_path: Option<PathBuf>,
22 pub log_level: Option<Level>,
23}
24
25fn parse_bool(s: &str) -> bool {
28 matches!(s.to_ascii_lowercase().as_str(), "1" | "true" | "yes")
29}
30
31fn parse_value_map(content: &str) -> HashMap<String, String> {
32 let mut map = HashMap::new();
33 for line in content.lines() {
34 let line = line.trim();
35 if line.is_empty() || line.starts_with('#') {
36 continue;
37 }
38 if let Some(eq) = line.find('=') {
39 let key = line[..eq].trim().to_string();
40 let val = line[eq + 1..].trim().to_string();
41 map.insert(key, val);
42 }
43 }
44 map
45}
46
47impl Config {
48 pub fn load(path: Option<PathBuf>) -> Self {
52 let mut cfg = Config::default();
53
54 if let Ok(v) = std::env::var("DRY_RUN") {
56 cfg.dry_run = parse_bool(&v);
57 }
58 if let Ok(v) = std::env::var("POLICY_PATH") {
59 cfg.policy_path = Some(PathBuf::from(v));
60 }
61 if let Ok(v) = std::env::var("SAVING_CPU_FREQ") {
62 cfg.saving_cpu_freq = Some(v);
63 }
64 if let Ok(v) = std::env::var("HOLD_TRIGGER_SEC") {
65 cfg.hold_trigger_sec = v.parse::<f32>().ok();
66 }
67 if let Ok(v) = std::env::var("TOGGLE_WIFI") {
68 cfg.toggle_wifi = parse_bool(&v);
69 }
70 if let Ok(v) = std::env::var("WIFI_RFKILL") {
71 cfg.wifi_rfkill_path = Some(PathBuf::from(v));
72 }
73 if let Ok(v) = std::env::var("LOG_LEVEL")
74 && let Ok(l) = v.parse::<log::Level>()
75 {
76 cfg.log_level = Some(l);
77 }
78
79 let cfg_path = if let Some(p) = path {
81 p
82 } else if PathBuf::from("./etc/uconsole-sleep/config.default").exists() {
83 PathBuf::from("./etc/uconsole-sleep/config.default")
84 } else {
85 PathBuf::from("/etc/uconsole-sleep/config")
86 };
87
88 if let Ok(content) = fs::read_to_string(&cfg_path) {
89 let map = parse_value_map(&content);
90 if let Some(v) = map.get("DRY_RUN") {
91 cfg.dry_run = parse_bool(v);
92 }
93 if let Some(v) = map.get("POLICY_PATH") {
94 cfg.policy_path = Some(PathBuf::from(v));
95 }
96 if let Some(v) = map.get("SAVING_CPU_FREQ") {
97 cfg.saving_cpu_freq = Some(v.clone());
98 }
99 if let Some(v) = map.get("HOLD_TRIGGER_SEC") {
100 cfg.hold_trigger_sec = v.parse::<f32>().ok();
101 }
102 if let Some(v) = map.get("TOGGLE_WIFI") {
103 cfg.toggle_wifi = parse_bool(v);
104 }
105 if let Some(v) = map.get("WIFI_RFKILL") {
106 cfg.wifi_rfkill_path = Some(PathBuf::from(v));
107 }
108 if let Some(v) = map.get("LOG_LEVEL")
109 && let Ok(l) = v.parse::<log::Level>()
110 {
111 cfg.log_level = Some(l);
112 }
113 }
114
115 if cfg.toggle_wifi && cfg.wifi_rfkill_path.is_none() {
117 cfg.wifi_rfkill_path = Some(PathBuf::from(wifi::RFKILL_PATH));
118 }
119
120 cfg
121 }
122
123 #[cfg(test)]
124 pub fn load_test_file(path: &std::path::Path) -> Self {
125 Config::load(Some(path.to_path_buf()))
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use crate::wifi;
132
133 use super::*;
134 use std::env;
135 use std::fs;
136
137 #[test]
138 fn test_load_from_repo_default() {
139 let c = Config::load(None);
141 assert!(c.saving_cpu_freq.is_some());
142 assert_eq!(c.saving_cpu_freq.unwrap(), "100,600");
143 assert_eq!(c.hold_trigger_sec.unwrap(), 0.7_f32);
144 }
145
146 #[test]
147 fn test_wifi_default_rfkill() {
148 let tmp = env::temp_dir().join(format!(
149 "uconsole_cfg_{}",
150 std::time::SystemTime::now()
151 .duration_since(std::time::UNIX_EPOCH)
152 .unwrap()
153 .as_millis()
154 ));
155 let _ = fs::create_dir_all(&tmp);
156 let cfg_file = tmp.join("cfg");
157 fs::write(&cfg_file, "TOGGLE_WIFI=true\n").unwrap();
158 let cfg = Config::load(Some(cfg_file.clone()));
159 assert!(cfg.toggle_wifi);
160 assert_eq!(
161 cfg.wifi_rfkill_path.unwrap(),
162 PathBuf::from(wifi::RFKILL_PATH)
163 );
164 }
165
166 #[test]
167 fn test_log_level_from_file() {
168 let tmp = env::temp_dir().join(format!(
169 "uconsole_cfg_{}",
170 std::time::SystemTime::now()
171 .duration_since(std::time::UNIX_EPOCH)
172 .unwrap()
173 .as_millis()
174 ));
175 let _ = fs::create_dir_all(&tmp);
176 let cfg_file = tmp.join("cfg_log");
177 fs::write(&cfg_file, "LOG_LEVEL=debug\n").unwrap();
178 let cfg = Config::load(Some(cfg_file.clone()));
179 assert_eq!(cfg.log_level, Some(log::Level::Debug));
180 }
181
182 }