1pub mod palette;
3
4pub use palette::Palette;
5
6use crate::Color;
7
8use std::borrow::Cow;
9use std::fmt;
10use std::sync::Arc;
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum Theme {
15 Light,
17 Dark,
19 Dracula,
21 Nord,
23 SolarizedLight,
25 SolarizedDark,
27 GruvboxLight,
29 GruvboxDark,
31 CatppuccinLatte,
33 CatppuccinFrappe,
35 CatppuccinMacchiato,
37 CatppuccinMocha,
39 TokyoNight,
41 TokyoNightStorm,
43 TokyoNightLight,
45 KanagawaWave,
47 KanagawaDragon,
49 KanagawaLotus,
51 Moonfly,
53 Nightfly,
55 Oxocarbon,
57 Ferra,
59 Custom(Arc<Custom>),
61}
62
63impl Theme {
64 pub const ALL: &'static [Self] = &[
66 Self::Light,
67 Self::Dark,
68 Self::Dracula,
69 Self::Nord,
70 Self::SolarizedLight,
71 Self::SolarizedDark,
72 Self::GruvboxLight,
73 Self::GruvboxDark,
74 Self::CatppuccinLatte,
75 Self::CatppuccinFrappe,
76 Self::CatppuccinMacchiato,
77 Self::CatppuccinMocha,
78 Self::TokyoNight,
79 Self::TokyoNightStorm,
80 Self::TokyoNightLight,
81 Self::KanagawaWave,
82 Self::KanagawaDragon,
83 Self::KanagawaLotus,
84 Self::Moonfly,
85 Self::Nightfly,
86 Self::Oxocarbon,
87 Self::Ferra,
88 ];
89
90 pub fn custom(name: impl Into<Cow<'static, str>>, palette: Palette) -> Self {
92 Self::custom_with_fn(name, palette, palette::Extended::generate)
93 }
94
95 pub fn custom_with_fn(
98 name: impl Into<Cow<'static, str>>,
99 palette: Palette,
100 generate: impl FnOnce(Palette) -> palette::Extended,
101 ) -> Self {
102 Self::Custom(Arc::new(Custom::with_fn(name, palette, generate)))
103 }
104
105 pub fn palette(&self) -> Palette {
107 match self {
108 Self::Light => Palette::LIGHT,
109 Self::Dark => Palette::DARK,
110 Self::Dracula => Palette::DRACULA,
111 Self::Nord => Palette::NORD,
112 Self::SolarizedLight => Palette::SOLARIZED_LIGHT,
113 Self::SolarizedDark => Palette::SOLARIZED_DARK,
114 Self::GruvboxLight => Palette::GRUVBOX_LIGHT,
115 Self::GruvboxDark => Palette::GRUVBOX_DARK,
116 Self::CatppuccinLatte => Palette::CATPPUCCIN_LATTE,
117 Self::CatppuccinFrappe => Palette::CATPPUCCIN_FRAPPE,
118 Self::CatppuccinMacchiato => Palette::CATPPUCCIN_MACCHIATO,
119 Self::CatppuccinMocha => Palette::CATPPUCCIN_MOCHA,
120 Self::TokyoNight => Palette::TOKYO_NIGHT,
121 Self::TokyoNightStorm => Palette::TOKYO_NIGHT_STORM,
122 Self::TokyoNightLight => Palette::TOKYO_NIGHT_LIGHT,
123 Self::KanagawaWave => Palette::KANAGAWA_WAVE,
124 Self::KanagawaDragon => Palette::KANAGAWA_DRAGON,
125 Self::KanagawaLotus => Palette::KANAGAWA_LOTUS,
126 Self::Moonfly => Palette::MOONFLY,
127 Self::Nightfly => Palette::NIGHTFLY,
128 Self::Oxocarbon => Palette::OXOCARBON,
129 Self::Ferra => Palette::FERRA,
130 Self::Custom(custom) => custom.palette,
131 }
132 }
133
134 pub fn extended_palette(&self) -> &palette::Extended {
136 match self {
137 Self::Light => &palette::EXTENDED_LIGHT,
138 Self::Dark => &palette::EXTENDED_DARK,
139 Self::Dracula => &palette::EXTENDED_DRACULA,
140 Self::Nord => &palette::EXTENDED_NORD,
141 Self::SolarizedLight => &palette::EXTENDED_SOLARIZED_LIGHT,
142 Self::SolarizedDark => &palette::EXTENDED_SOLARIZED_DARK,
143 Self::GruvboxLight => &palette::EXTENDED_GRUVBOX_LIGHT,
144 Self::GruvboxDark => &palette::EXTENDED_GRUVBOX_DARK,
145 Self::CatppuccinLatte => &palette::EXTENDED_CATPPUCCIN_LATTE,
146 Self::CatppuccinFrappe => &palette::EXTENDED_CATPPUCCIN_FRAPPE,
147 Self::CatppuccinMacchiato => &palette::EXTENDED_CATPPUCCIN_MACCHIATO,
148 Self::CatppuccinMocha => &palette::EXTENDED_CATPPUCCIN_MOCHA,
149 Self::TokyoNight => &palette::EXTENDED_TOKYO_NIGHT,
150 Self::TokyoNightStorm => &palette::EXTENDED_TOKYO_NIGHT_STORM,
151 Self::TokyoNightLight => &palette::EXTENDED_TOKYO_NIGHT_LIGHT,
152 Self::KanagawaWave => &palette::EXTENDED_KANAGAWA_WAVE,
153 Self::KanagawaDragon => &palette::EXTENDED_KANAGAWA_DRAGON,
154 Self::KanagawaLotus => &palette::EXTENDED_KANAGAWA_LOTUS,
155 Self::Moonfly => &palette::EXTENDED_MOONFLY,
156 Self::Nightfly => &palette::EXTENDED_NIGHTFLY,
157 Self::Oxocarbon => &palette::EXTENDED_OXOCARBON,
158 Self::Ferra => &palette::EXTENDED_FERRA,
159 Self::Custom(custom) => &custom.extended,
160 }
161 }
162}
163
164impl fmt::Display for Theme {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.write_str(self.name())
167 }
168}
169
170#[derive(Debug, Clone, PartialEq)]
172pub struct Custom {
173 name: Cow<'static, str>,
174 palette: Palette,
175 extended: palette::Extended,
176}
177
178impl Custom {
179 pub fn new(name: String, palette: Palette) -> Self {
181 Self::with_fn(name, palette, palette::Extended::generate)
182 }
183
184 pub fn with_fn(
187 name: impl Into<Cow<'static, str>>,
188 palette: Palette,
189 generate: impl FnOnce(Palette) -> palette::Extended,
190 ) -> Self {
191 Self {
192 name: name.into(),
193 palette,
194 extended: generate(palette),
195 }
196 }
197}
198
199impl fmt::Display for Custom {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 write!(f, "{}", self.name)
202 }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
207pub enum Mode {
208 #[default]
210 None,
211 Light,
213 Dark,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq)]
219pub struct Style {
220 pub background_color: Color,
222
223 pub text_color: Color,
225}
226
227pub trait Base {
229 fn default(preference: Mode) -> Self;
231
232 fn mode(&self) -> Mode;
234
235 fn base(&self) -> Style;
237
238 fn palette(&self) -> Option<Palette>;
244
245 fn name(&self) -> &str;
250}
251
252impl Base for Theme {
253 fn default(preference: Mode) -> Self {
254 use std::env;
255 use std::sync::OnceLock;
256
257 static SYSTEM: OnceLock<Option<Theme>> = OnceLock::new();
258
259 let system = SYSTEM.get_or_init(|| {
260 let name = env::var("ICED_THEME").ok()?;
261
262 Theme::ALL
263 .iter()
264 .find(|theme| theme.to_string() == name)
265 .cloned()
266 });
267
268 if let Some(system) = system {
269 return system.clone();
270 }
271
272 match preference {
273 Mode::None | Mode::Light => Self::Light,
274 Mode::Dark => Self::Dark,
275 }
276 }
277
278 fn mode(&self) -> Mode {
279 if self.extended_palette().is_dark {
280 Mode::Dark
281 } else {
282 Mode::Light
283 }
284 }
285
286 fn base(&self) -> Style {
287 default(self)
288 }
289
290 fn palette(&self) -> Option<Palette> {
291 Some(self.palette())
292 }
293
294 fn name(&self) -> &str {
295 match self {
296 Self::Light => "Light",
297 Self::Dark => "Dark",
298 Self::Dracula => "Dracula",
299 Self::Nord => "Nord",
300 Self::SolarizedLight => "Solarized Light",
301 Self::SolarizedDark => "Solarized Dark",
302 Self::GruvboxLight => "Gruvbox Light",
303 Self::GruvboxDark => "Gruvbox Dark",
304 Self::CatppuccinLatte => "Catppuccin Latte",
305 Self::CatppuccinFrappe => "Catppuccin Frappé",
306 Self::CatppuccinMacchiato => "Catppuccin Macchiato",
307 Self::CatppuccinMocha => "Catppuccin Mocha",
308 Self::TokyoNight => "Tokyo Night",
309 Self::TokyoNightStorm => "Tokyo Night Storm",
310 Self::TokyoNightLight => "Tokyo Night Light",
311 Self::KanagawaWave => "Kanagawa Wave",
312 Self::KanagawaDragon => "Kanagawa Dragon",
313 Self::KanagawaLotus => "Kanagawa Lotus",
314 Self::Moonfly => "Moonfly",
315 Self::Nightfly => "Nightfly",
316 Self::Oxocarbon => "Oxocarbon",
317 Self::Ferra => "Ferra",
318 Self::Custom(custom) => &custom.name,
319 }
320 }
321}
322
323pub fn default(theme: &Theme) -> Style {
325 let palette = theme.extended_palette();
326
327 Style {
328 background_color: palette.background.base.color,
329 text_color: palette.background.base.text,
330 }
331}