aaai_core/profile/
prefs.rs1use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "lowercase")]
13pub enum Theme {
14 #[default]
16 Light,
17 Dark,
19 System,
21}
22
23impl std::fmt::Display for Theme {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 Theme::Light => write!(f, "Light"),
27 Theme::Dark => write!(f, "Dark"),
28 Theme::System => write!(f, "System"),
29 }
30 }
31}
32
33#[derive(Debug, Clone, Default, Serialize, Deserialize)]
35pub struct UserPrefs {
36 #[serde(default)]
38 pub theme: Theme,
39
40 #[serde(default)]
43 pub language: String,
44
45 #[serde(default = "default_ignored_dirs")]
50 pub global_ignored_dirs: Vec<String>,
51}
52
53fn default_ignored_dirs() -> Vec<String> {
54 vec![
55 ".git".into(),
56 "target".into(),
57 "node_modules".into(),
58 ".DS_Store".into(),
59 ]
60}
61
62impl UserPrefs {
63 fn path() -> anyhow::Result<PathBuf> {
64 let base = dirs::home_dir()
65 .ok_or_else(|| anyhow::anyhow!("Cannot determine home directory"))?
66 .join(".aaai");
67 std::fs::create_dir_all(&base)?;
68 Ok(base.join("prefs.yaml"))
69 }
70
71 pub fn load() -> Self {
73 match Self::path().and_then(|p| {
74 if !p.exists() { return Ok(Self::default()); }
75 let text = std::fs::read_to_string(&p)?;
76 serde_yaml::from_str(&text).map_err(|e| anyhow::anyhow!(e))
77 }) {
78 Ok(prefs) => prefs,
79 Err(e) => {
80 log::warn!("Could not load prefs: {e}");
81 Self::default()
82 }
83 }
84 }
85
86 pub fn save(&self) {
88 if let Err(e) = Self::path().and_then(|p| {
89 let yaml = serde_yaml::to_string(self).map_err(|e| anyhow::anyhow!(e))?;
90 std::fs::write(&p, yaml)?;
91 Ok(())
92 }) {
93 log::warn!("Could not save prefs: {e}");
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn round_trip_yaml() {
104 let prefs = UserPrefs { theme: Theme::Dark, ..Default::default() };
105 let yaml = serde_yaml::to_string(&prefs).unwrap();
106 let restored: UserPrefs = serde_yaml::from_str(&yaml).unwrap();
107 assert_eq!(restored.theme, Theme::Dark);
108 }
109
110 #[test]
111 fn default_is_light() {
112 assert_eq!(UserPrefs::default().theme, Theme::Light);
113 }
114
115 #[test]
116 fn display_names() {
117 assert_eq!(Theme::Light.to_string(), "Light");
118 assert_eq!(Theme::Dark.to_string(), "Dark");
119 }
120
121 #[test]
124 fn new_fields_round_trip() {
125 let p = UserPrefs {
126 language: "ja".into(),
127 global_ignored_dirs: vec![".git".into(), "target".into()],
128 ..Default::default()
129 };
130 let yaml = serde_yaml::to_string(&p).unwrap();
131 let r: UserPrefs = serde_yaml::from_str(&yaml).unwrap();
132 assert_eq!(r.language, "ja");
133 assert_eq!(r.global_ignored_dirs, vec![".git", "target"]);
134 }
135
136 #[test]
137 fn missing_fields_get_defaults() {
138 let yaml = "theme: light\n";
140 let p: UserPrefs = serde_yaml::from_str(yaml).unwrap();
141 assert!(!p.global_ignored_dirs.is_empty(), "default dirs should be applied");
142 assert_eq!(p.language, "", "absent language should be empty string");
143 }
144}