ricecoder_tui/
theme_reset.rs

1//! Theme reset manager for resetting themes to defaults
2
3use crate::style::{Color, Theme};
4use anyhow::{anyhow, Result};
5use std::collections::HashMap;
6use std::sync::Arc;
7
8/// Theme reset manager for resetting themes to their default state
9pub struct ThemeResetManager {
10    /// Default built-in themes for reference
11    default_themes: Arc<HashMap<String, Theme>>,
12}
13
14impl ThemeResetManager {
15    /// Create a new theme reset manager
16    pub fn new() -> Self {
17        let mut defaults = HashMap::new();
18
19        // Store all built-in themes as defaults
20        defaults.insert("dark".to_string(), Theme::default());
21        defaults.insert("light".to_string(), Theme::light());
22        defaults.insert("monokai".to_string(), Theme::monokai());
23        defaults.insert("dracula".to_string(), Theme::dracula());
24        defaults.insert("nord".to_string(), Theme::nord());
25        defaults.insert("high-contrast".to_string(), Theme::high_contrast());
26
27        Self {
28            default_themes: Arc::new(defaults),
29        }
30    }
31
32    /// Reset all colors in a theme to their default values
33    ///
34    /// # Arguments
35    ///
36    /// * `theme` - The theme to reset
37    ///
38    /// # Returns
39    ///
40    /// * `Ok(())` - If reset was successful
41    /// * `Err` - If the theme is not a built-in theme or reset failed
42    pub fn reset_colors(&self, theme: &mut Theme) -> Result<()> {
43        let default = self
44            .default_themes
45            .get(&theme.name)
46            .ok_or_else(|| anyhow!("Theme '{}' is not a built-in theme", theme.name))?;
47
48        // Reset all color fields to defaults
49        theme.primary = default.primary;
50        theme.secondary = default.secondary;
51        theme.accent = default.accent;
52        theme.background = default.background;
53        theme.foreground = default.foreground;
54        theme.error = default.error;
55        theme.warning = default.warning;
56        theme.success = default.success;
57
58        Ok(())
59    }
60
61    /// Reset an entire theme to its built-in default
62    ///
63    /// # Arguments
64    ///
65    /// * `theme` - The theme to reset
66    ///
67    /// # Returns
68    ///
69    /// * `Ok(())` - If reset was successful
70    /// * `Err` - If the theme is not a built-in theme or reset failed
71    pub fn reset_theme(&self, theme: &mut Theme) -> Result<()> {
72        let default = self
73            .default_themes
74            .get(&theme.name)
75            .ok_or_else(|| anyhow!("Theme '{}' is not a built-in theme", theme.name))?;
76
77        // Replace entire theme with default
78        *theme = default.clone();
79
80        Ok(())
81    }
82
83    /// Get the default color value for a specific color field in a theme
84    ///
85    /// # Arguments
86    ///
87    /// * `theme_name` - The name of the theme
88    /// * `color_name` - The name of the color field (e.g., "primary", "error", "background")
89    ///
90    /// # Returns
91    ///
92    /// * `Ok(Color)` - The default color value
93    /// * `Err` - If the theme or color field is not found
94    pub fn get_default_color(&self, theme_name: &str, color_name: &str) -> Result<Color> {
95        let theme = self
96            .default_themes
97            .get(theme_name)
98            .ok_or_else(|| anyhow!("Theme '{}' is not a built-in theme", theme_name))?;
99
100        match color_name.to_lowercase().as_str() {
101            "primary" => Ok(theme.primary),
102            "secondary" => Ok(theme.secondary),
103            "accent" => Ok(theme.accent),
104            "background" => Ok(theme.background),
105            "foreground" => Ok(theme.foreground),
106            "error" => Ok(theme.error),
107            "warning" => Ok(theme.warning),
108            "success" => Ok(theme.success),
109            _ => Err(anyhow!("Unknown color field: {}", color_name)),
110        }
111    }
112
113    /// Reset a specific color field in a theme to its default value
114    ///
115    /// # Arguments
116    ///
117    /// * `theme` - The theme to modify
118    /// * `color_name` - The name of the color field to reset
119    ///
120    /// # Returns
121    ///
122    /// * `Ok(())` - If reset was successful
123    /// * `Err` - If the theme or color field is not found
124    pub fn reset_color(&self, theme: &mut Theme, color_name: &str) -> Result<()> {
125        let default_color = self.get_default_color(&theme.name, color_name)?;
126
127        match color_name.to_lowercase().as_str() {
128            "primary" => theme.primary = default_color,
129            "secondary" => theme.secondary = default_color,
130            "accent" => theme.accent = default_color,
131            "background" => theme.background = default_color,
132            "foreground" => theme.foreground = default_color,
133            "error" => theme.error = default_color,
134            "warning" => theme.warning = default_color,
135            "success" => theme.success = default_color,
136            _ => return Err(anyhow!("Unknown color field: {}", color_name)),
137        }
138
139        Ok(())
140    }
141
142    /// Check if a theme is a built-in theme
143    ///
144    /// # Arguments
145    ///
146    /// * `theme_name` - The name of the theme
147    ///
148    /// # Returns
149    ///
150    /// * `true` - If the theme is a built-in theme
151    /// * `false` - Otherwise
152    pub fn is_builtin_theme(&self, theme_name: &str) -> bool {
153        self.default_themes.contains_key(theme_name)
154    }
155
156    /// Get all built-in theme names
157    ///
158    /// # Returns
159    ///
160    /// A vector of built-in theme names
161    pub fn builtin_theme_names(&self) -> Vec<String> {
162        let mut names: Vec<_> = self.default_themes.keys().cloned().collect();
163        names.sort();
164        names
165    }
166
167    /// Get a copy of a built-in theme by name
168    ///
169    /// # Arguments
170    ///
171    /// * `theme_name` - The name of the theme
172    ///
173    /// # Returns
174    ///
175    /// * `Ok(Theme)` - A copy of the built-in theme
176    /// * `Err` - If the theme is not found
177    pub fn get_builtin_theme(&self, theme_name: &str) -> Result<Theme> {
178        self.default_themes
179            .get(theme_name)
180            .cloned()
181            .ok_or_else(|| anyhow!("Theme '{}' is not a built-in theme", theme_name))
182    }
183}
184
185impl Default for ThemeResetManager {
186    fn default() -> Self {
187        Self::new()
188    }
189}
190
191impl std::fmt::Debug for ThemeResetManager {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        f.debug_struct("ThemeResetManager")
194            .field("builtin_themes", &self.builtin_theme_names())
195            .finish()
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202
203    #[test]
204    fn test_reset_manager_creation() {
205        let manager = ThemeResetManager::new();
206        assert_eq!(manager.builtin_theme_names().len(), 6);
207    }
208
209    #[test]
210    fn test_reset_colors() {
211        let manager = ThemeResetManager::new();
212        let mut theme = Theme::default();
213
214        // Modify colors
215        theme.primary = Color::new(255, 0, 0);
216        theme.error = Color::new(0, 255, 0);
217
218        // Reset colors
219        manager.reset_colors(&mut theme).unwrap();
220
221        // Verify colors are reset to defaults
222        let default = Theme::default();
223        assert_eq!(theme.primary, default.primary);
224        assert_eq!(theme.error, default.error);
225    }
226
227    #[test]
228    fn test_reset_theme() {
229        let manager = ThemeResetManager::new();
230        let mut theme = Theme::light();
231
232        // Modify theme
233        theme.primary = Color::new(255, 0, 0);
234        theme.background = Color::new(100, 100, 100);
235
236        // Reset theme
237        manager.reset_theme(&mut theme).unwrap();
238
239        // Verify theme is reset to defaults
240        let default = Theme::light();
241        assert_eq!(theme.primary, default.primary);
242        assert_eq!(theme.secondary, default.secondary);
243        assert_eq!(theme.accent, default.accent);
244        assert_eq!(theme.background, default.background);
245        assert_eq!(theme.foreground, default.foreground);
246        assert_eq!(theme.error, default.error);
247        assert_eq!(theme.warning, default.warning);
248        assert_eq!(theme.success, default.success);
249    }
250
251    #[test]
252    fn test_reset_theme_preserves_name() {
253        let manager = ThemeResetManager::new();
254        let mut theme = Theme::monokai();
255        let original_name = theme.name.clone();
256
257        // Modify theme
258        theme.primary = Color::new(255, 0, 0);
259
260        // Reset theme
261        manager.reset_theme(&mut theme).unwrap();
262
263        // Verify name is preserved
264        assert_eq!(theme.name, original_name);
265    }
266
267    #[test]
268    fn test_get_default_color() {
269        let manager = ThemeResetManager::new();
270        let default_primary = manager.get_default_color("dark", "primary").unwrap();
271        let theme = Theme::default();
272        assert_eq!(default_primary, theme.primary);
273    }
274
275    #[test]
276    fn test_get_default_color_all_fields() {
277        let manager = ThemeResetManager::new();
278        let theme = Theme::dracula();
279
280        assert_eq!(
281            manager.get_default_color("dracula", "primary").unwrap(),
282            theme.primary
283        );
284        assert_eq!(
285            manager.get_default_color("dracula", "secondary").unwrap(),
286            theme.secondary
287        );
288        assert_eq!(
289            manager.get_default_color("dracula", "accent").unwrap(),
290            theme.accent
291        );
292        assert_eq!(
293            manager.get_default_color("dracula", "background").unwrap(),
294            theme.background
295        );
296        assert_eq!(
297            manager.get_default_color("dracula", "foreground").unwrap(),
298            theme.foreground
299        );
300        assert_eq!(
301            manager.get_default_color("dracula", "error").unwrap(),
302            theme.error
303        );
304        assert_eq!(
305            manager.get_default_color("dracula", "warning").unwrap(),
306            theme.warning
307        );
308        assert_eq!(
309            manager.get_default_color("dracula", "success").unwrap(),
310            theme.success
311        );
312    }
313
314    #[test]
315    fn test_get_default_color_case_insensitive() {
316        let manager = ThemeResetManager::new();
317        let color1 = manager.get_default_color("dark", "primary").unwrap();
318        let color2 = manager.get_default_color("dark", "PRIMARY").unwrap();
319        let color3 = manager.get_default_color("dark", "Primary").unwrap();
320
321        assert_eq!(color1, color2);
322        assert_eq!(color2, color3);
323    }
324
325    #[test]
326    fn test_get_default_color_invalid_theme() {
327        let manager = ThemeResetManager::new();
328        assert!(manager.get_default_color("invalid", "primary").is_err());
329    }
330
331    #[test]
332    fn test_get_default_color_invalid_field() {
333        let manager = ThemeResetManager::new();
334        assert!(manager.get_default_color("dark", "invalid").is_err());
335    }
336
337    #[test]
338    fn test_reset_color() {
339        let manager = ThemeResetManager::new();
340        let mut theme = Theme::nord();
341        let default_primary = manager.get_default_color("nord", "primary").unwrap();
342
343        // Modify primary color
344        theme.primary = Color::new(255, 0, 0);
345        assert_ne!(theme.primary, default_primary);
346
347        // Reset primary color
348        manager.reset_color(&mut theme, "primary").unwrap();
349
350        // Verify primary color is reset
351        assert_eq!(theme.primary, default_primary);
352
353        // Verify other colors are unchanged
354        let default_error = manager.get_default_color("nord", "error").unwrap();
355        assert_eq!(theme.error, default_error);
356    }
357
358    #[test]
359    fn test_reset_color_invalid_field() {
360        let manager = ThemeResetManager::new();
361        let mut theme = Theme::default();
362        assert!(manager.reset_color(&mut theme, "invalid").is_err());
363    }
364
365    #[test]
366    fn test_is_builtin_theme() {
367        let manager = ThemeResetManager::new();
368        assert!(manager.is_builtin_theme("dark"));
369        assert!(manager.is_builtin_theme("light"));
370        assert!(manager.is_builtin_theme("monokai"));
371        assert!(manager.is_builtin_theme("dracula"));
372        assert!(manager.is_builtin_theme("nord"));
373        assert!(manager.is_builtin_theme("high-contrast"));
374        assert!(!manager.is_builtin_theme("custom"));
375        assert!(!manager.is_builtin_theme("invalid"));
376    }
377
378    #[test]
379    fn test_builtin_theme_names() {
380        let manager = ThemeResetManager::new();
381        let names = manager.builtin_theme_names();
382        assert_eq!(names.len(), 6);
383        assert!(names.contains(&"dark".to_string()));
384        assert!(names.contains(&"light".to_string()));
385        assert!(names.contains(&"monokai".to_string()));
386        assert!(names.contains(&"dracula".to_string()));
387        assert!(names.contains(&"nord".to_string()));
388        assert!(names.contains(&"high-contrast".to_string()));
389    }
390
391    #[test]
392    fn test_get_builtin_theme() {
393        let manager = ThemeResetManager::new();
394        let theme = manager.get_builtin_theme("dark").unwrap();
395        assert_eq!(theme.name, "dark");
396        let default = Theme::default();
397        assert_eq!(theme.primary, default.primary);
398        assert_eq!(theme.secondary, default.secondary);
399    }
400
401    #[test]
402    fn test_get_builtin_theme_invalid() {
403        let manager = ThemeResetManager::new();
404        assert!(manager.get_builtin_theme("invalid").is_err());
405    }
406
407    #[test]
408    fn test_reset_colors_invalid_theme() {
409        let manager = ThemeResetManager::new();
410        let mut theme = Theme::light();
411        theme.name = "custom".to_string();
412
413        // Should fail because "custom" is not a built-in theme
414        assert!(manager.reset_colors(&mut theme).is_err());
415    }
416
417    #[test]
418    fn test_reset_theme_invalid_theme() {
419        let manager = ThemeResetManager::new();
420        let mut theme = Theme::light();
421        theme.name = "custom".to_string();
422
423        // Should fail because "custom" is not a built-in theme
424        assert!(manager.reset_theme(&mut theme).is_err());
425    }
426
427    #[test]
428    fn test_reset_all_themes() {
429        let manager = ThemeResetManager::new();
430
431        for theme_name in manager.builtin_theme_names() {
432            let mut theme = manager.get_builtin_theme(&theme_name).unwrap();
433
434            // Modify all colors
435            theme.primary = Color::new(255, 0, 0);
436            theme.secondary = Color::new(0, 255, 0);
437            theme.accent = Color::new(0, 0, 255);
438            theme.background = Color::new(100, 100, 100);
439            theme.foreground = Color::new(200, 200, 200);
440            theme.error = Color::new(255, 128, 128);
441            theme.warning = Color::new(255, 255, 128);
442            theme.success = Color::new(128, 255, 128);
443
444            // Reset colors
445            manager.reset_colors(&mut theme).unwrap();
446
447            // Verify all colors are reset
448            let default = manager.get_builtin_theme(&theme_name).unwrap();
449            assert_eq!(theme.primary, default.primary);
450            assert_eq!(theme.secondary, default.secondary);
451            assert_eq!(theme.accent, default.accent);
452            assert_eq!(theme.background, default.background);
453            assert_eq!(theme.foreground, default.foreground);
454            assert_eq!(theme.error, default.error);
455            assert_eq!(theme.warning, default.warning);
456            assert_eq!(theme.success, default.success);
457        }
458    }
459}