1use std::{fs::File, path};
2
3use anyhow::Result;
4use ratatui::style::{Color, Style};
5use serde_yml::{from_reader, Value};
6
7use crate::common::{tilde, CONFIG_PATH, SYNTECT_DEFAULT_THEME};
8use crate::config::{Bindings, ColorG};
9
10#[derive(Debug, Clone)]
15pub struct Config {
16 pub binds: Bindings,
18}
19
20impl Default for Config {
21 fn default() -> Self {
23 Self {
24 binds: Bindings::default(),
25 }
26 }
27}
28
29impl Config {
30 fn update_from_config(&mut self, yaml: &Value) -> Result<()> {
32 self.binds.update_normal(&yaml["keys"]);
33 self.binds.update_custom(&yaml["custom"]);
34 Ok(())
35 }
36}
37
38pub fn load_config(path: &str) -> Result<Config> {
46 let mut config = Config::default();
47 let Ok(file) = File::open(path::Path::new(&tilde(path).to_string())) else {
48 crate::log_info!("Couldn't read config file at {path}");
49 return Ok(config);
50 };
51 let Ok(yaml) = from_reader(file) else {
52 return Ok(config);
53 };
54 let _ = config.update_from_config(&yaml);
55 Ok(config)
56}
57
58pub fn read_normal_file_colorer() -> (ColorG, ColorG) {
71 let default_pair = (ColorG::new(0, 255, 0), ColorG::new(0, 0, 255));
72 let Ok(file) = File::open(tilde(CONFIG_PATH).as_ref()) else {
73 return default_pair;
74 };
75 let Ok(yaml) = from_reader::<File, Value>(file) else {
76 return default_pair;
77 };
78 let Some(start) = yaml["colors"]["normal_start"].as_str() else {
79 return default_pair;
80 };
81 let Some(stop) = yaml["colors"]["normal_stop"].as_str() else {
82 return default_pair;
83 };
84 let Some(start_color) = ColorG::parse_any_color(start) else {
85 return default_pair;
86 };
87 let Some(stop_color) = ColorG::parse_any_color(stop) else {
88 return default_pair;
89 };
90 (start_color, stop_color)
91}
92macro_rules! update_style {
93 ($self_style:expr, $yaml:ident, $key:expr) => {
94 if let Some(color) = read_yaml_string($yaml, $key) {
95 $self_style = crate::config::str_to_ratatui(color).into();
96 }
97 };
98}
99
100fn read_yaml_string(yaml: &Value, key: &str) -> Option<String> {
101 yaml[key].as_str().map(|s| s.to_string())
102}
103
104#[derive(Debug, Clone)]
107pub struct FileStyle {
108 pub directory: Style,
110 pub block: Style,
112 pub char: Style,
114 pub fifo: Style,
116 pub socket: Style,
118 pub symlink: Style,
120 pub broken: Style,
122}
123
124impl FileStyle {
125 fn new() -> Self {
126 Self {
127 directory: Color::Red.into(),
128 block: Color::Yellow.into(),
129 char: Color::Green.into(),
130 fifo: Color::Blue.into(),
131 socket: Color::Cyan.into(),
132 symlink: Color::Magenta.into(),
133 broken: Color::White.into(),
134 }
135 }
136
137 fn update_values(&mut self, yaml: &Value) {
139 update_style!(self.directory, yaml, "directory");
140 update_style!(self.block, yaml, "block");
141 update_style!(self.char, yaml, "char");
142 update_style!(self.fifo, yaml, "fifo");
143 update_style!(self.socket, yaml, "socket");
144 update_style!(self.symlink, yaml, "symlink");
145 update_style!(self.broken, yaml, "broken");
146 }
147
148 fn update_from_config(&mut self) {
149 let Ok(file) = File::open(std::path::Path::new(&tilde(CONFIG_PATH).to_string())) else {
150 return;
151 };
152 let Ok(yaml) = from_reader::<File, Value>(file) else {
153 return;
154 };
155 self.update_values(&yaml["colors"]);
156 }
157
158 pub fn from_config() -> Self {
159 let mut style = Self::default();
160 style.update_from_config();
161 style
162 }
163}
164
165impl Default for FileStyle {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171pub struct MenuStyle {
173 pub first: Style,
174 pub second: Style,
175 pub selected_border: Style,
176 pub inert_border: Style,
177 pub palette_1: Style,
178 pub palette_2: Style,
179 pub palette_3: Style,
180 pub palette_4: Style,
181}
182
183impl Default for MenuStyle {
184 fn default() -> Self {
185 Self {
186 first: Color::Rgb(45, 250, 209).into(),
187 second: Color::Rgb(230, 189, 87).into(),
188 selected_border: Color::Rgb(45, 250, 209).into(),
189 inert_border: Color::Rgb(248, 248, 248).into(),
190 palette_1: Color::Rgb(45, 250, 209).into(),
191 palette_2: Color::Rgb(230, 189, 87).into(),
192 palette_3: Color::Rgb(230, 167, 255).into(),
193 palette_4: Color::Rgb(59, 204, 255).into(),
194 }
195 }
196}
197
198impl MenuStyle {
199 pub fn update(mut self) -> Self {
200 if let Ok(file) = File::open(path::Path::new(&tilde(CONFIG_PATH).to_string())) {
201 if let Ok(yaml) = from_reader::<File, Value>(file) {
202 let menu_colors = &yaml["colors"];
203 update_style!(self.first, menu_colors, "header_first");
204 update_style!(self.second, menu_colors, "header_second");
205 update_style!(self.selected_border, menu_colors, "selected_border");
206 update_style!(self.inert_border, menu_colors, "inert_border");
207 update_style!(self.palette_1, menu_colors, "palette_1");
208 update_style!(self.palette_2, menu_colors, "palette_2");
209 update_style!(self.palette_3, menu_colors, "palette_3");
210 update_style!(self.palette_4, menu_colors, "palette_4");
211 }
212 }
213 self
214 }
215
216 #[inline]
217 pub const fn palette(&self) -> [Style; 4] {
218 [
219 self.palette_1,
220 self.palette_2,
221 self.palette_3,
222 self.palette_4,
223 ]
224 }
225
226 #[inline]
227 pub const fn palette_size(&self) -> usize {
228 self.palette().len()
229 }
230}
231
232#[derive(Debug)]
234pub struct SyntectTheme {
235 pub name: String,
236}
237
238impl Default for SyntectTheme {
239 fn default() -> Self {
240 Self {
241 name: SYNTECT_DEFAULT_THEME.to_owned(),
242 }
243 }
244}
245
246impl SyntectTheme {
247 pub fn from_config(path: &str) -> Result<Self> {
248 let Ok(file) = File::open(path::Path::new(&tilde(path).to_string())) else {
249 crate::log_info!("Couldn't read config file at {path}");
250 return Ok(Self::default());
251 };
252 let Ok(yaml) = from_reader::<File, Value>(file) else {
253 return Ok(Self::default());
254 };
255 let Some(name) = yaml["syntect_theme"].as_str() else {
256 return Ok(Self::default());
257 };
258 crate::log_info!("Config: found syntect theme: {name}");
259
260 Ok(Self {
261 name: name.to_string(),
262 })
263 }
264}