use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use crate::types::*;
#[derive(Clone, Serialize, Deserialize)]
pub struct Prefs {
pub sort_mode: SortMode,
pub sort_rev: bool,
pub show_local: bool,
pub refresh_rate: u64,
pub bar_style: BarStyle,
pub color_mode: ColorMode,
pub thresh_warn: u8,
pub thresh_crit: u8,
pub show_bars: bool,
pub show_border: bool,
pub show_header: bool,
pub compact: bool,
pub show_used: bool,
pub full_mount: bool,
#[serde(default = "default_true")]
pub show_all: bool,
#[serde(default)]
pub unit_mode: UnitMode,
#[serde(default)]
pub col_mount_w: u16,
#[serde(default)]
pub col_bar_end_w: u16,
#[serde(default)]
pub col_pct_w: u16,
#[serde(default)]
pub custom_themes: HashMap<String, ThemeColors>,
#[serde(default)]
pub active_theme: Option<String>,
#[serde(default)]
pub bookmarks: Vec<String>,
}
fn default_true() -> bool { true }
impl Default for Prefs {
fn default() -> Self {
Self {
sort_mode: SortMode::Name,
sort_rev: false,
show_local: false,
refresh_rate: 1,
bar_style: BarStyle::Gradient,
color_mode: ColorMode::Default,
thresh_warn: 70,
thresh_crit: 90,
show_bars: true,
show_border: true,
show_header: true,
compact: false,
show_used: true,
full_mount: false,
show_all: true,
unit_mode: UnitMode::Human,
col_mount_w: 0,
col_bar_end_w: 0,
col_pct_w: 0,
custom_themes: HashMap::new(),
active_theme: None,
bookmarks: Vec::new(),
}
}
}
fn prefs_path() -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".storageshower.conf")
}
const DEFAULT_CONF: &str = include_str!("../storageshower.default.conf");
pub fn load_prefs() -> Prefs {
load_prefs_from(None)
}
pub fn load_prefs_from(custom_path: Option<&str>) -> Prefs {
let path = match custom_path {
Some(p) => PathBuf::from(p),
None => prefs_path(),
};
if !path.exists() && custom_path.is_none() {
let _ = std::fs::write(&path, DEFAULT_CONF);
}
std::fs::read_to_string(&path)
.ok()
.and_then(|s| toml::from_str(&s).ok())
.unwrap_or_default()
}
pub fn save_prefs(p: &Prefs) {
if let Ok(s) = toml::to_string_pretty(p) {
let _ = std::fs::write(prefs_path(), s);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_prefs_values() {
let p = Prefs::default();
assert_eq!(p.sort_mode, SortMode::Name);
assert!(!p.sort_rev);
assert!(!p.show_local);
assert_eq!(p.refresh_rate, 1);
assert_eq!(p.bar_style, BarStyle::Gradient);
assert_eq!(p.color_mode, ColorMode::Default);
assert_eq!(p.thresh_warn, 70);
assert_eq!(p.thresh_crit, 90);
assert!(p.show_bars);
assert!(p.show_border);
assert!(p.show_header);
assert!(!p.compact);
assert!(p.show_used);
assert!(!p.full_mount);
assert!(p.show_all);
assert_eq!(p.unit_mode, UnitMode::Human);
assert_eq!(p.col_mount_w, 0);
assert_eq!(p.col_bar_end_w, 0);
assert_eq!(p.col_pct_w, 0);
}
#[test]
fn prefs_roundtrip_toml() {
let mut p = Prefs::default();
p.sort_mode = SortMode::Size;
p.sort_rev = true;
p.bar_style = BarStyle::Ascii;
p.color_mode = ColorMode::Purple;
p.thresh_warn = 60;
p.thresh_crit = 85;
p.unit_mode = UnitMode::GiB;
p.col_mount_w = 25;
p.col_bar_end_w = 30;
p.col_pct_w = 7;
p.refresh_rate = 5;
let serialized = toml::to_string_pretty(&p).unwrap();
let deserialized: Prefs = toml::from_str(&serialized).unwrap();
assert_eq!(deserialized.sort_mode, SortMode::Size);
assert!(deserialized.sort_rev);
assert_eq!(deserialized.bar_style, BarStyle::Ascii);
assert_eq!(deserialized.color_mode, ColorMode::Purple);
assert_eq!(deserialized.thresh_warn, 60);
assert_eq!(deserialized.thresh_crit, 85);
assert_eq!(deserialized.unit_mode, UnitMode::GiB);
assert_eq!(deserialized.col_mount_w, 25);
assert_eq!(deserialized.col_bar_end_w, 30);
assert_eq!(deserialized.col_pct_w, 7);
assert_eq!(deserialized.refresh_rate, 5);
}
#[test]
fn prefs_deserialize_missing_fields_uses_defaults() {
let toml_str = r#"
sort_mode = "Pct"
sort_rev = false
show_local = false
refresh_rate = 2
bar_style = "Solid"
color_mode = "Green"
thresh_warn = 50
thresh_crit = 80
show_bars = true
show_border = false
show_header = true
compact = true
show_used = false
full_mount = true
"#;
let p: Prefs = toml::from_str(toml_str).unwrap();
assert_eq!(p.sort_mode, SortMode::Pct);
assert_eq!(p.bar_style, BarStyle::Solid);
assert!(p.compact);
assert!(!p.show_border);
assert!(p.show_all); assert_eq!(p.unit_mode, UnitMode::Human);
assert_eq!(p.col_mount_w, 0);
assert_eq!(p.col_bar_end_w, 0);
assert_eq!(p.col_pct_w, 0);
}
#[test]
fn default_conf_parses() {
let p: Result<Prefs, _> = toml::from_str(DEFAULT_CONF);
assert!(p.is_ok(), "Default config should parse: {:?}", p.err());
}
}