1use crate::theme::Theme;
4
5#[derive(Debug, Clone, PartialEq)]
19pub struct ThemeSwitcher {
20 themes: Vec<Theme>,
21 index: usize,
22}
23
24impl Default for ThemeSwitcher {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl ThemeSwitcher {
31 #[must_use]
33 pub fn new() -> Self {
34 Self {
35 themes: Theme::all(),
36 index: 0,
37 }
38 }
39
40 #[must_use]
42 pub fn with_themes(themes: Vec<Theme>) -> Self {
43 if themes.is_empty() {
44 Self::new()
45 } else {
46 Self { themes, index: 0 }
47 }
48 }
49
50 #[must_use]
52 pub fn current(&self) -> &Theme {
53 &self.themes[self.index]
54 }
55
56 #[must_use]
58 pub fn index(&self) -> usize {
59 self.index
60 }
61
62 #[must_use]
64 pub fn len(&self) -> usize {
65 self.themes.len()
66 }
67
68 #[must_use]
70 pub fn is_empty(&self) -> bool {
71 false
72 }
73
74 #[must_use]
76 pub fn themes(&self) -> &[Theme] {
77 &self.themes
78 }
79
80 #[must_use]
82 pub fn names(&self) -> Vec<&str> {
83 self.themes.iter().map(|t| t.name.as_str()).collect()
84 }
85
86 #[allow(clippy::should_implement_trait)]
88 pub fn next(&mut self) -> &Theme {
89 self.index = (self.index + 1) % self.themes.len();
90 self.current()
91 }
92
93 pub fn prev(&mut self) -> &Theme {
95 self.index = (self.index + self.themes.len() - 1) % self.themes.len();
96 self.current()
97 }
98
99 pub fn set_index(&mut self, index: usize) -> &Theme {
101 self.index = index % self.themes.len();
102 self.current()
103 }
104
105 pub fn set_by_name(&mut self, name: &str) -> bool {
108 let key = name.trim().to_ascii_lowercase().replace(['_', ' '], "-");
109 match self.themes.iter().position(|t| t.name == key) {
110 Some(i) => {
111 self.index = i;
112 true
113 }
114 None => false,
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn starts_at_first() {
125 let s = ThemeSwitcher::new();
126 assert_eq!(s.index(), 0);
127 assert_eq!(s.len(), Theme::all().len());
128 assert!(!s.is_empty());
129 }
130
131 #[test]
132 fn next_wraps() {
133 let mut s = ThemeSwitcher::new();
134 for _ in 0..s.len() {
135 s.next();
136 }
137 assert_eq!(s.index(), 0);
138 }
139
140 #[test]
141 fn prev_wraps() {
142 let mut s = ThemeSwitcher::new();
143 s.prev();
144 assert_eq!(s.index(), s.len() - 1);
145 }
146
147 #[test]
148 fn set_index_wraps() {
149 let mut s = ThemeSwitcher::new();
150 s.set_index(s.len() + 2);
151 assert_eq!(s.index(), 2);
152 }
153
154 #[test]
155 fn set_by_name_found_and_missing() {
156 let mut s = ThemeSwitcher::new();
157 assert!(s.set_by_name("Tokyo Night"));
158 assert_eq!(s.current().name, "tokyo-night");
159 assert!(!s.set_by_name("zzz"));
160 assert_eq!(s.current().name, "tokyo-night");
161 }
162
163 #[test]
164 fn custom_and_empty() {
165 let s = ThemeSwitcher::with_themes(vec![Theme::default().name("a")]);
166 assert_eq!(s.names(), vec!["a"]);
167 assert_eq!(ThemeSwitcher::with_themes(vec![]).len(), Theme::all().len());
168 let mut one = s;
169 one.next();
170 assert_eq!(one.index(), 0);
171 }
172}