pacsea/theme/config/
settings_save.rs1use std::fs;
2use std::path::Path;
3
4use crate::theme::config::skeletons::SETTINGS_SKELETON_CONTENT;
5use crate::theme::paths::resolve_settings_config_path;
6
7pub fn save_sort_mode(sm: crate::state::SortMode) {
19 let path = resolve_settings_config_path().or_else(|| {
20 std::env::var("XDG_CONFIG_HOME")
21 .ok()
22 .map(std::path::PathBuf::from)
23 .or_else(|| {
24 std::env::var("HOME")
25 .ok()
26 .map(|h| Path::new(&h).join(".config"))
27 })
28 .map(|base| base.join("pacsea").join("settings.conf"))
29 });
30 let Some(p) = path else {
31 return;
32 };
33
34 if let Some(dir) = p.parent() {
36 let _ = fs::create_dir_all(dir);
37 }
38
39 let meta = std::fs::metadata(&p).ok();
41 let file_exists = meta.is_some();
42 let file_empty = meta.is_none_or(|m| m.len() == 0);
43
44 let mut lines: Vec<String> = if file_exists && !file_empty {
45 fs::read_to_string(&p)
47 .map(|content| content.lines().map(ToString::to_string).collect())
48 .unwrap_or_default()
49 } else {
50 SETTINGS_SKELETON_CONTENT
52 .lines()
53 .map(ToString::to_string)
54 .collect()
55 };
56 let mut replaced = false;
57 for line in &mut lines {
58 let trimmed = line.trim();
59 if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
60 continue;
61 }
62 if let Some(eq) = trimmed.find('=') {
63 let (kraw, _) = trimmed.split_at(eq);
64 let key = kraw.trim().to_lowercase().replace(['.', '-', ' '], "_");
65 if key == "sort_mode" || key == "results_sort" {
66 *line = format!("sort_mode = {}", sm.as_config_key());
67 replaced = true;
68 }
69 }
70 }
71 if !replaced {
72 if let Some(dir) = p.parent() {
73 let _ = fs::create_dir_all(dir);
74 }
75 lines.push(format!("sort_mode = {}", sm.as_config_key()));
76 }
77 let new_content = if lines.is_empty() {
78 format!("sort_mode = {}\n", sm.as_config_key())
79 } else {
80 lines.join("\n")
81 };
82 let _ = fs::write(p, new_content);
83}
84
85fn save_boolean_key_with_aliases(primary_key: &str, aliases: &[&str], value: bool) {
100 let path = resolve_settings_config_path().or_else(|| {
101 std::env::var("XDG_CONFIG_HOME")
102 .ok()
103 .map(std::path::PathBuf::from)
104 .or_else(|| {
105 std::env::var("HOME")
106 .ok()
107 .map(|h| Path::new(&h).join(".config"))
108 })
109 .map(|base| base.join("pacsea").join("settings.conf"))
110 });
111 let Some(p) = path else {
112 return;
113 };
114
115 if let Some(dir) = p.parent() {
117 let _ = fs::create_dir_all(dir);
118 }
119
120 let meta = std::fs::metadata(&p).ok();
122 let file_exists = meta.is_some();
123 let file_empty = meta.is_none_or(|m| m.len() == 0);
124
125 let mut lines: Vec<String> = if file_exists && !file_empty {
126 fs::read_to_string(&p)
128 .map(|content| content.lines().map(ToString::to_string).collect())
129 .unwrap_or_default()
130 } else {
131 SETTINGS_SKELETON_CONTENT
133 .lines()
134 .map(ToString::to_string)
135 .collect()
136 };
137 let bool_text = if value { "true" } else { "false" };
138 let primary_norm = primary_key
139 .trim()
140 .to_lowercase()
141 .replace(['.', '-', ' '], "_");
142 let alias_norms: Vec<String> = aliases
143 .iter()
144 .map(|k| k.trim().to_lowercase().replace(['.', '-', ' '], "_"))
145 .collect();
146 let mut replaced = false;
147 for line in &mut lines {
148 let trimmed = line.trim();
149 if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
150 continue;
151 }
152 if let Some(eq) = trimmed.find('=') {
153 let (kraw, _) = trimmed.split_at(eq);
154 let key = kraw.trim().to_lowercase().replace(['.', '-', ' '], "_");
155 if key == primary_norm || alias_norms.iter().any(|alias| alias == &key) {
156 *line = format!("{primary_key} = {bool_text}");
157 replaced = true;
158 }
159 }
160 }
161 if !replaced {
162 if let Some(dir) = p.parent() {
163 let _ = fs::create_dir_all(dir);
164 }
165 lines.push(format!("{primary_key} = {bool_text}"));
166 }
167 let new_content = if lines.is_empty() {
168 format!("{primary_key} = {bool_text}\n")
169 } else {
170 lines.join("\n")
171 };
172 let _ = fs::write(p, new_content);
173}
174
175fn save_boolean_key(key_norm: &str, value: bool) {
187 save_boolean_key_with_aliases(key_norm, &[], value);
188}
189
190fn save_string_key(key_norm: &str, value: &str) {
203 let path = resolve_settings_config_path().or_else(|| {
204 std::env::var("XDG_CONFIG_HOME")
205 .ok()
206 .map(std::path::PathBuf::from)
207 .or_else(|| {
208 std::env::var("HOME")
209 .ok()
210 .map(|h| Path::new(&h).join(".config"))
211 })
212 .map(|base| base.join("pacsea").join("settings.conf"))
213 });
214 let Some(p) = path else {
215 return;
216 };
217
218 if let Some(dir) = p.parent() {
220 let _ = fs::create_dir_all(dir);
221 }
222
223 let meta = std::fs::metadata(&p).ok();
225 let file_exists = meta.is_some();
226 let file_empty = meta.is_none_or(|m| m.len() == 0);
227
228 let mut lines: Vec<String> = if file_exists && !file_empty {
229 fs::read_to_string(&p)
231 .map(|content| content.lines().map(ToString::to_string).collect())
232 .unwrap_or_default()
233 } else {
234 SETTINGS_SKELETON_CONTENT
236 .lines()
237 .map(ToString::to_string)
238 .collect()
239 };
240 let mut replaced = false;
241 for line in &mut lines {
242 let trimmed = line.trim();
243 if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
244 continue;
245 }
246 if let Some(eq) = trimmed.find('=') {
247 let (kraw, _) = trimmed.split_at(eq);
248 let key = kraw.trim().to_lowercase().replace(['.', '-', ' '], "_");
249 if key == key_norm {
250 *line = format!("{key_norm} = {value}");
251 replaced = true;
252 }
253 }
254 }
255 if !replaced {
256 if let Some(dir) = p.parent() {
257 let _ = fs::create_dir_all(dir);
258 }
259 lines.push(format!("{key_norm} = {value}"));
260 }
261 let new_content = if lines.is_empty() {
262 format!("{key_norm} = {value}\n")
263 } else {
264 lines.join("\n")
265 };
266 let _ = fs::write(p, new_content);
267}
268
269pub fn save_show_recent_pane(value: bool) {
281 save_boolean_key_with_aliases("show_search_history_pane", &["show_recent_pane"], value);
282}
283pub fn save_show_install_pane(value: bool) {
294 save_boolean_key("show_install_pane", value);
295}
296pub fn save_show_keybinds_footer(value: bool) {
307 save_boolean_key("show_keybinds_footer", value);
308}
309
310pub fn save_selected_countries(value: &str) {
321 save_string_key("selected_countries", value);
322}
323pub fn save_mirror_count(value: u16) {
334 save_string_key("mirror_count", &value.to_string());
335}
336
337pub fn save_app_start_mode(start_in_news: bool) {
339 let v = if start_in_news { "news" } else { "package" };
340 save_string_key("app_start_mode", v);
341}
342
343pub fn save_news_filter_show_arch_news(value: bool) {
345 save_boolean_key("news_filter_show_arch_news", value);
346}
347
348pub fn save_news_filter_show_advisories(value: bool) {
350 save_boolean_key("news_filter_show_advisories", value);
351}
352
353pub fn save_news_filter_show_pkg_updates(value: bool) {
355 save_boolean_key("news_filter_show_pkg_updates", value);
356}
357
358pub fn save_news_filter_show_aur_updates(value: bool) {
360 save_boolean_key("news_filter_show_aur_updates", value);
361}
362
363pub fn save_news_filter_show_aur_comments(value: bool) {
365 save_boolean_key("news_filter_show_aur_comments", value);
366}
367
368pub fn save_news_filter_installed_only(value: bool) {
370 save_boolean_key("news_filter_installed_only", value);
371}
372
373pub fn save_news_filters_collapsed(value: bool) {
375 save_boolean_key("news_filters_collapsed", value);
376}
377
378pub fn save_news_max_age_days(value: Option<u32>) {
380 let v = value.map_or_else(|| "all".to_string(), |d| d.to_string());
381 save_string_key("news_max_age_days", &v);
382}
383
384pub fn save_startup_news_configured(value: bool) {
386 save_boolean_key("startup_news_configured", value);
387}
388
389pub fn save_startup_news_show_arch_news(value: bool) {
391 save_boolean_key("startup_news_show_arch_news", value);
392}
393
394pub fn save_startup_news_show_advisories(value: bool) {
396 save_boolean_key("startup_news_show_advisories", value);
397}
398
399pub fn save_startup_news_show_aur_updates(value: bool) {
401 save_boolean_key("startup_news_show_aur_updates", value);
402}
403
404pub fn save_startup_news_show_aur_comments(value: bool) {
406 save_boolean_key("startup_news_show_aur_comments", value);
407}
408
409pub fn save_startup_news_show_pkg_updates(value: bool) {
411 save_boolean_key("startup_news_show_pkg_updates", value);
412}
413
414pub fn save_startup_news_max_age_days(value: Option<u32>) {
416 let v = value.map_or_else(|| "all".to_string(), |d| d.to_string());
417 save_string_key("startup_news_max_age_days", &v);
418}
419
420pub fn save_virustotal_api_key(value: &str) {
431 save_string_key("virustotal_api_key", value);
432}
433
434pub fn save_scan_do_clamav(value: bool) {
445 save_boolean_key("scan_do_clamav", value);
446}
447pub fn save_scan_do_trivy(value: bool) {
458 save_boolean_key("scan_do_trivy", value);
459}
460pub fn save_scan_do_semgrep(value: bool) {
471 save_boolean_key("scan_do_semgrep", value);
472}
473pub fn save_scan_do_shellcheck(value: bool) {
484 save_boolean_key("scan_do_shellcheck", value);
485}
486pub fn save_scan_do_virustotal(value: bool) {
497 save_boolean_key("scan_do_virustotal", value);
498}
499pub fn save_scan_do_custom(value: bool) {
510 save_boolean_key("scan_do_custom", value);
511}
512
513pub fn save_scan_do_sleuth(value: bool) {
524 save_boolean_key("scan_do_sleuth", value);
525}
526
527pub fn save_fuzzy_search(value: bool) {
538 save_boolean_key("fuzzy_search", value);
539}