flashkraft_gui/core/
storage.rs1use iced::Theme;
7use sled::Db;
8use std::path::PathBuf;
9
10const THEME_KEY: &[u8] = b"theme";
12
13#[derive(Debug)]
15pub struct Storage {
16 db: Db,
17}
18
19impl Storage {
20 pub fn new() -> Result<Self, String> {
24 let db_path = Self::get_db_path()?;
25 let db = sled::open(db_path).map_err(|e| format!("Failed to open database: {}", e))?;
26 Ok(Self { db })
27 }
28
29 fn get_db_path() -> Result<PathBuf, String> {
33 let mut path =
34 dirs::config_dir().ok_or_else(|| "Could not determine config directory".to_string())?;
35 path.push("flashkraft");
36 std::fs::create_dir_all(&path)
37 .map_err(|e| format!("Failed to create config directory: {}", e))?;
38 path.push("preferences.db");
39 Ok(path)
40 }
41
42 pub fn load_theme(&self) -> Option<Theme> {
46 let value = self.db.get(THEME_KEY).ok()??;
47 let theme_name = String::from_utf8(value.to_vec()).ok()?;
48 Self::theme_from_string(&theme_name)
49 }
50
51 pub fn save_theme(&self, theme: &Theme) -> Result<(), String> {
55 let theme_name = Self::theme_to_string(theme);
56 self.db
57 .insert(THEME_KEY, theme_name.as_bytes())
58 .map_err(|e| format!("Failed to save theme: {}", e))?;
59 self.db
60 .flush()
61 .map_err(|e| format!("Failed to flush database: {}", e))?;
62 Ok(())
63 }
64
65 fn theme_to_string(theme: &Theme) -> String {
67 match theme {
68 Theme::Dark => "Dark",
69 Theme::Light => "Light",
70 Theme::Dracula => "Dracula",
71 Theme::Nord => "Nord",
72 Theme::SolarizedLight => "SolarizedLight",
73 Theme::SolarizedDark => "SolarizedDark",
74 Theme::GruvboxLight => "GruvboxLight",
75 Theme::GruvboxDark => "GruvboxDark",
76 Theme::CatppuccinLatte => "CatppuccinLatte",
77 Theme::CatppuccinFrappe => "CatppuccinFrappe",
78 Theme::CatppuccinMacchiato => "CatppuccinMacchiato",
79 Theme::CatppuccinMocha => "CatppuccinMocha",
80 Theme::TokyoNight => "TokyoNight",
81 Theme::TokyoNightStorm => "TokyoNightStorm",
82 Theme::TokyoNightLight => "TokyoNightLight",
83 Theme::KanagawaWave => "KanagawaWave",
84 Theme::KanagawaDragon => "KanagawaDragon",
85 Theme::KanagawaLotus => "KanagawaLotus",
86 Theme::Moonfly => "Moonfly",
87 Theme::Nightfly => "Nightfly",
88 Theme::Oxocarbon => "Oxocarbon",
89 _ => "Dark",
90 }
91 .to_string()
92 }
93
94 fn theme_from_string(s: &str) -> Option<Theme> {
96 match s {
97 "Dark" => Some(Theme::Dark),
98 "Light" => Some(Theme::Light),
99 "Dracula" => Some(Theme::Dracula),
100 "Nord" => Some(Theme::Nord),
101 "SolarizedLight" => Some(Theme::SolarizedLight),
102 "SolarizedDark" => Some(Theme::SolarizedDark),
103 "GruvboxLight" => Some(Theme::GruvboxLight),
104 "GruvboxDark" => Some(Theme::GruvboxDark),
105 "CatppuccinLatte" => Some(Theme::CatppuccinLatte),
106 "CatppuccinFrappe" => Some(Theme::CatppuccinFrappe),
107 "CatppuccinMacchiato" => Some(Theme::CatppuccinMacchiato),
108 "CatppuccinMocha" => Some(Theme::CatppuccinMocha),
109 "TokyoNight" => Some(Theme::TokyoNight),
110 "TokyoNightStorm" => Some(Theme::TokyoNightStorm),
111 "TokyoNightLight" => Some(Theme::TokyoNightLight),
112 "KanagawaWave" => Some(Theme::KanagawaWave),
113 "KanagawaDragon" => Some(Theme::KanagawaDragon),
114 "KanagawaLotus" => Some(Theme::KanagawaLotus),
115 "Moonfly" => Some(Theme::Moonfly),
116 "Nightfly" => Some(Theme::Nightfly),
117 "Oxocarbon" => Some(Theme::Oxocarbon),
118 _ => None,
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_theme_to_string() {
129 assert_eq!(Storage::theme_to_string(&Theme::Dark), "Dark");
130 assert_eq!(Storage::theme_to_string(&Theme::Light), "Light");
131 assert_eq!(Storage::theme_to_string(&Theme::Dracula), "Dracula");
132 }
133
134 #[test]
135 fn test_theme_from_string() {
136 assert!(matches!(
137 Storage::theme_from_string("Dark"),
138 Some(Theme::Dark)
139 ));
140 assert!(matches!(
141 Storage::theme_from_string("Light"),
142 Some(Theme::Light)
143 ));
144 assert!(Storage::theme_from_string("Invalid").is_none());
145 }
146
147 #[test]
148 fn test_roundtrip() {
149 let themes = vec![
150 Theme::Dark,
151 Theme::Light,
152 Theme::Dracula,
153 Theme::Nord,
154 Theme::CatppuccinMocha,
155 ];
156
157 for theme in themes {
158 let name = Storage::theme_to_string(&theme);
159 let restored = Storage::theme_from_string(&name);
160 assert!(restored.is_some());
161 }
162 }
163}