use std::fs;
use std::path::Path;
use crate::theme::config::skeletons::SETTINGS_SKELETON_CONTENT;
use crate::theme::paths::resolve_settings_config_path;
pub fn save_sort_mode(sm: crate::state::SortMode) {
let path = resolve_settings_config_path().or_else(|| {
std::env::var("XDG_CONFIG_HOME")
.ok()
.map(std::path::PathBuf::from)
.or_else(|| {
std::env::var("HOME")
.ok()
.map(|h| Path::new(&h).join(".config"))
})
.map(|base| base.join("pacsea").join("settings.conf"))
});
let Some(p) = path else {
return;
};
if let Some(dir) = p.parent() {
let _ = fs::create_dir_all(dir);
}
let meta = std::fs::metadata(&p).ok();
let file_exists = meta.is_some();
let file_empty = meta.is_none_or(|m| m.len() == 0);
let mut lines: Vec<String> = if file_exists && !file_empty {
fs::read_to_string(&p)
.map(|content| content.lines().map(ToString::to_string).collect())
.unwrap_or_default()
} else {
SETTINGS_SKELETON_CONTENT
.lines()
.map(ToString::to_string)
.collect()
};
let mut replaced = false;
for line in &mut lines {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
continue;
}
if let Some(eq) = trimmed.find('=') {
let (kraw, _) = trimmed.split_at(eq);
let key = kraw.trim().to_lowercase().replace(['.', '-', ' '], "_");
if key == "sort_mode" || key == "results_sort" {
*line = format!("sort_mode = {}", sm.as_config_key());
replaced = true;
}
}
}
if !replaced {
if let Some(dir) = p.parent() {
let _ = fs::create_dir_all(dir);
}
lines.push(format!("sort_mode = {}", sm.as_config_key()));
}
let new_content = if lines.is_empty() {
format!("sort_mode = {}\n", sm.as_config_key())
} else {
lines.join("\n")
};
let _ = fs::write(p, new_content);
}
fn save_boolean_key_with_aliases(primary_key: &str, aliases: &[&str], value: bool) {
let path = resolve_settings_config_path().or_else(|| {
std::env::var("XDG_CONFIG_HOME")
.ok()
.map(std::path::PathBuf::from)
.or_else(|| {
std::env::var("HOME")
.ok()
.map(|h| Path::new(&h).join(".config"))
})
.map(|base| base.join("pacsea").join("settings.conf"))
});
let Some(p) = path else {
return;
};
if let Some(dir) = p.parent() {
let _ = fs::create_dir_all(dir);
}
let meta = std::fs::metadata(&p).ok();
let file_exists = meta.is_some();
let file_empty = meta.is_none_or(|m| m.len() == 0);
let mut lines: Vec<String> = if file_exists && !file_empty {
fs::read_to_string(&p)
.map(|content| content.lines().map(ToString::to_string).collect())
.unwrap_or_default()
} else {
SETTINGS_SKELETON_CONTENT
.lines()
.map(ToString::to_string)
.collect()
};
let bool_text = if value { "true" } else { "false" };
let primary_norm = primary_key
.trim()
.to_lowercase()
.replace(['.', '-', ' '], "_");
let alias_norms: Vec<String> = aliases
.iter()
.map(|k| k.trim().to_lowercase().replace(['.', '-', ' '], "_"))
.collect();
let mut replaced = false;
for line in &mut lines {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
continue;
}
if let Some(eq) = trimmed.find('=') {
let (kraw, _) = trimmed.split_at(eq);
let key = kraw.trim().to_lowercase().replace(['.', '-', ' '], "_");
if key == primary_norm || alias_norms.iter().any(|alias| alias == &key) {
*line = format!("{primary_key} = {bool_text}");
replaced = true;
}
}
}
if !replaced {
if let Some(dir) = p.parent() {
let _ = fs::create_dir_all(dir);
}
lines.push(format!("{primary_key} = {bool_text}"));
}
let new_content = if lines.is_empty() {
format!("{primary_key} = {bool_text}\n")
} else {
lines.join("\n")
};
let _ = fs::write(p, new_content);
}
fn save_boolean_key(key_norm: &str, value: bool) {
save_boolean_key_with_aliases(key_norm, &[], value);
}
fn save_string_key(key_norm: &str, value: &str) {
let path = resolve_settings_config_path().or_else(|| {
std::env::var("XDG_CONFIG_HOME")
.ok()
.map(std::path::PathBuf::from)
.or_else(|| {
std::env::var("HOME")
.ok()
.map(|h| Path::new(&h).join(".config"))
})
.map(|base| base.join("pacsea").join("settings.conf"))
});
let Some(p) = path else {
return;
};
if let Some(dir) = p.parent() {
let _ = fs::create_dir_all(dir);
}
let meta = std::fs::metadata(&p).ok();
let file_exists = meta.is_some();
let file_empty = meta.is_none_or(|m| m.len() == 0);
let mut lines: Vec<String> = if file_exists && !file_empty {
fs::read_to_string(&p)
.map(|content| content.lines().map(ToString::to_string).collect())
.unwrap_or_default()
} else {
SETTINGS_SKELETON_CONTENT
.lines()
.map(ToString::to_string)
.collect()
};
let mut replaced = false;
for line in &mut lines {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
continue;
}
if let Some(eq) = trimmed.find('=') {
let (kraw, _) = trimmed.split_at(eq);
let key = kraw.trim().to_lowercase().replace(['.', '-', ' '], "_");
if key == key_norm {
*line = format!("{key_norm} = {value}");
replaced = true;
}
}
}
if !replaced {
if let Some(dir) = p.parent() {
let _ = fs::create_dir_all(dir);
}
lines.push(format!("{key_norm} = {value}"));
}
let new_content = if lines.is_empty() {
format!("{key_norm} = {value}\n")
} else {
lines.join("\n")
};
let _ = fs::write(p, new_content);
}
pub fn save_show_recent_pane(value: bool) {
save_boolean_key_with_aliases("show_search_history_pane", &["show_recent_pane"], value);
}
pub fn save_show_install_pane(value: bool) {
save_boolean_key("show_install_pane", value);
}
pub fn save_show_keybinds_footer(value: bool) {
save_boolean_key("show_keybinds_footer", value);
}
pub fn save_selected_countries(value: &str) {
save_string_key("selected_countries", value);
}
pub fn save_mirror_count(value: u16) {
save_string_key("mirror_count", &value.to_string());
}
pub fn save_app_start_mode(start_in_news: bool) {
let v = if start_in_news { "news" } else { "package" };
save_string_key("app_start_mode", v);
}
pub fn save_news_filter_show_arch_news(value: bool) {
save_boolean_key("news_filter_show_arch_news", value);
}
pub fn save_news_filter_show_advisories(value: bool) {
save_boolean_key("news_filter_show_advisories", value);
}
pub fn save_news_filter_show_pkg_updates(value: bool) {
save_boolean_key("news_filter_show_pkg_updates", value);
}
pub fn save_news_filter_show_aur_updates(value: bool) {
save_boolean_key("news_filter_show_aur_updates", value);
}
pub fn save_news_filter_show_aur_comments(value: bool) {
save_boolean_key("news_filter_show_aur_comments", value);
}
pub fn save_news_filter_installed_only(value: bool) {
save_boolean_key("news_filter_installed_only", value);
}
pub fn save_news_filters_collapsed(value: bool) {
save_boolean_key("news_filters_collapsed", value);
}
pub fn save_news_max_age_days(value: Option<u32>) {
let v = value.map_or_else(|| "all".to_string(), |d| d.to_string());
save_string_key("news_max_age_days", &v);
}
pub fn save_startup_news_configured(value: bool) {
save_boolean_key("startup_news_configured", value);
}
pub fn save_startup_news_show_arch_news(value: bool) {
save_boolean_key("startup_news_show_arch_news", value);
}
pub fn save_startup_news_show_advisories(value: bool) {
save_boolean_key("startup_news_show_advisories", value);
}
pub fn save_startup_news_show_aur_updates(value: bool) {
save_boolean_key("startup_news_show_aur_updates", value);
}
pub fn save_startup_news_show_aur_comments(value: bool) {
save_boolean_key("startup_news_show_aur_comments", value);
}
pub fn save_startup_news_show_pkg_updates(value: bool) {
save_boolean_key("startup_news_show_pkg_updates", value);
}
pub fn save_startup_news_max_age_days(value: Option<u32>) {
let v = value.map_or_else(|| "all".to_string(), |d| d.to_string());
save_string_key("startup_news_max_age_days", &v);
}
pub fn save_virustotal_api_key(value: &str) {
save_string_key("virustotal_api_key", value);
}
pub fn save_scan_do_clamav(value: bool) {
save_boolean_key("scan_do_clamav", value);
}
pub fn save_scan_do_trivy(value: bool) {
save_boolean_key("scan_do_trivy", value);
}
pub fn save_scan_do_semgrep(value: bool) {
save_boolean_key("scan_do_semgrep", value);
}
pub fn save_scan_do_shellcheck(value: bool) {
save_boolean_key("scan_do_shellcheck", value);
}
pub fn save_scan_do_virustotal(value: bool) {
save_boolean_key("scan_do_virustotal", value);
}
pub fn save_scan_do_custom(value: bool) {
save_boolean_key("scan_do_custom", value);
}
pub fn save_scan_do_sleuth(value: bool) {
save_boolean_key("scan_do_sleuth", value);
}
pub fn save_fuzzy_search(value: bool) {
save_boolean_key("fuzzy_search", value);
}
pub fn save_results_filter_show_canonical(canonical_id: &str, value: bool) {
let trimmed = canonical_id.trim();
if trimmed.is_empty()
|| !trimmed
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
{
return;
}
let key = format!("results_filter_show_{trimmed}");
save_boolean_key_with_aliases(&key, &[], value);
}