1use std::{collections::HashMap, error::Error, str::FromStr};
2
3use serde::Deserialize;
4
5use crate::{color::Color, set_fg_bg, theme::LineNumbers, Theme};
6
7#[derive(Debug, Deserialize)]
8pub struct Skin {
9 #[serde(default)]
10 pub colors: HashMap<String, Color>,
11 #[serde(default)]
12 pub base: Base,
13 #[serde(default)]
14 pub title: Title,
15 #[serde(default)]
16 pub highlight: Highlight,
17 #[serde(default)]
18 pub border: Border,
19 #[serde(default)]
20 pub footer: Footer,
21 #[serde(default)]
22 pub status: Status,
23 #[serde(default)]
24 pub editor: Editor,
25}
26
27impl Default for Skin {
28 fn default() -> Self {
29 toml::from_str(include_str!("../assets/default.toml")).unwrap()
30 }
31}
32
33impl Skin {
34 pub(crate) fn from_file(file_path: &str) -> Result<Self, Box<dyn Error>> {
35 let f = shellexpand::env(file_path).map_or(file_path.to_string(), |x| x.to_string());
36
37 let toml_content = std::fs::read_to_string(f)?;
38
39 Ok(toml::from_str(&toml_content)?)
40 }
41
42 #[allow(clippy::too_many_lines)]
43 pub(crate) fn apply_to(&self, theme: &mut Theme) {
44 if let Some(target) = &self.base.focused {
46 set_fg_bg!(theme.base.focused, target, self.colors);
47 }
48 if let Some(target) = &self.base.unfocused {
49 set_fg_bg!(theme.base.unfocused, target, self.colors);
50 }
51
52 if let Some(target) = &self.highlight.focused {
54 set_fg_bg!(theme.highlight.focused, target, self.colors);
55 }
56 if let Some(target) = &self.highlight.unfocused {
57 set_fg_bg!(theme.highlight.unfocused, target, self.colors);
58 }
59
60 if let Some(target) = &self.border.unfocused {
62 set_fg_bg!(theme.border.unfocused, target, self.colors);
63 }
64 if let Some(target) = &self.border.focused {
65 set_fg_bg!(theme.border.focused, target, self.colors);
66 }
67
68 if let Some(target) = &self.title.focused {
70 set_fg_bg!(theme.title.focused, target, self.colors);
71 }
72 if let Some(target) = &self.title.unfocused {
73 set_fg_bg!(theme.title.unfocused, target, self.colors);
74 }
75
76 if let Some(hide_footer) = self.footer.hide {
78 theme.hide_footer = hide_footer;
79 } else {
80 theme.hide_footer = false;
81 }
82
83 if let Some(hide_status) = self.status.hide {
85 theme.hide_status = hide_status;
86 } else {
87 theme.hide_status = false;
88 }
89
90 if let Some(line_numbers) = self.editor.line_numbers {
92 theme.editor.line_numbers = line_numbers;
93 }
94 }
95}
96
97#[derive(Default, Debug, Deserialize)]
98pub(crate) struct Base {
99 pub focused: Option<FgBg>,
100 pub unfocused: Option<FgBg>,
101}
102
103#[derive(Default, Debug, Deserialize)]
104pub(crate) struct Highlight {
105 pub focused: Option<FgBg>,
106 pub unfocused: Option<FgBg>,
107}
108
109#[derive(Default, Debug, Deserialize)]
110pub(crate) struct Title {
111 pub focused: Option<FgBg>,
112 pub unfocused: Option<FgBg>,
113}
114
115#[derive(Debug, Deserialize, Default)]
116pub(crate) struct Border {
117 pub focused: Option<FgBg>,
118 pub unfocused: Option<FgBg>,
119}
120
121#[derive(Debug, Deserialize, Default)]
122pub(crate) struct Footer {
123 hide: Option<bool>,
124}
125
126#[derive(Debug, Deserialize, Default)]
127pub(crate) struct Status {
128 hide: Option<bool>,
129}
130
131#[derive(Debug, Deserialize, Default)]
132pub(crate) struct Editor {
133 pub line_numbers: Option<LineNumbers>,
134}
135
136#[derive(Debug, Deserialize, Default)]
137pub(crate) struct FgBg {
138 pub foreground: Option<String>,
139 pub background: Option<String>,
140}
141
142pub(crate) fn resolve_color(colors: &HashMap<String, Color>, color: Option<&str>) -> Option<Color> {
143 let color = color?;
144
145 if let Some(color) = colors.get(color) {
146 return Some(*color);
147 }
148
149 Color::from_str(color).ok()
150}
151
152pub(crate) mod macros {
153 #[macro_export]
154 macro_rules! set_fg_bg {
155 ($theme:expr, $fg_bg:expr, $colors:expr) => {
156 let fc = resolve_color(&$colors, $fg_bg.foreground.as_deref());
157 let bc = resolve_color(&$colors, $fg_bg.background.as_deref());
158 if let Some(fc) = fc {
159 $theme = $theme.fg(fc.0);
160 }
161 if let Some(bc) = bc {
162 $theme = $theme.bg(bc.0);
163 }
164 };
165 }
166}