gnostr_asyncgit/gitui/config/
mod.rs1use std::{collections::BTreeMap, path::PathBuf};
2
3use crate::{gitui::gitui_error::Error, gitui::menu::Menu, gitui::ops::Op, gitui::Res};
4use etcetera::{choose_base_strategy, BaseStrategy};
5use figment::{
6 providers::{Format, Toml},
7 Figment,
8};
9use ratatui::style::{Color, Modifier, Style};
10use serde::Deserialize;
11
12const DEFAULT_CONFIG: &str = include_str!("../../default_config.toml");
14
15#[derive(Default, Debug, Deserialize)]
17pub(crate) struct Config {
18 pub general: GeneralConfig,
20 pub style: StyleConfig,
22 pub bindings: BTreeMap<Menu, BTreeMap<Op, Vec<String>>>,
24}
25
26#[derive(Default, Debug, Deserialize)]
28pub struct GeneralConfig {
29 pub always_show_help: BoolConfigEntry,
31 pub confirm_quit: BoolConfigEntry,
33 pub refresh_on_file_change: BoolConfigEntry,
35 pub collapsed_sections: Vec<String>,
37}
38
39#[derive(Default, Debug, Deserialize)]
41pub struct BoolConfigEntry {
42 #[serde(default)]
44 pub enabled: bool,
45}
46
47#[derive(Default, Debug, Deserialize)]
49pub struct StyleConfig {
50 pub section_header: StyleConfigEntry,
52 pub file_header: StyleConfigEntry,
54 pub hunk_header: StyleConfigEntry,
56
57 #[serde(default)]
59 pub diff_highlight: DiffHighlightConfig,
60
61 #[serde(default)]
63 pub syntax_highlight: SyntaxHighlightConfig,
64
65 pub cursor: SymbolStyleConfigEntry,
67 pub selection_line: StyleConfigEntry,
69 pub selection_bar: SymbolStyleConfigEntry,
71 pub selection_area: StyleConfigEntry,
73
74 pub hash: StyleConfigEntry,
76 pub branch: StyleConfigEntry,
78 pub remote: StyleConfigEntry,
80 pub tag: StyleConfigEntry,
82
83 pub command: StyleConfigEntry,
85 pub active_arg: StyleConfigEntry,
87 pub hotkey: StyleConfigEntry,
89}
90
91#[derive(Default, Debug, Deserialize)]
93pub struct DiffHighlightConfig {
94 #[serde(default)]
96 pub tag_old: StyleConfigEntry,
97 #[serde(default)]
99 pub tag_new: StyleConfigEntry,
100 #[serde(default)]
102 pub unchanged_old: StyleConfigEntry,
103 #[serde(default)]
105 pub unchanged_new: StyleConfigEntry,
106 #[serde(default)]
108 pub changed_old: StyleConfigEntry,
109 #[serde(default)]
111 pub changed_new: StyleConfigEntry,
112}
113
114#[derive(Default, Debug, Deserialize)]
116pub struct SyntaxHighlightConfig {
117 #[serde(default)]
119 pub enabled: bool,
120 #[serde(default)]
122 pub attribute: StyleConfigEntry,
123 #[serde(default)]
125 pub comment: StyleConfigEntry,
126 #[serde(default)]
128 pub constant_builtin: StyleConfigEntry,
129 #[serde(default)]
131 pub constant: StyleConfigEntry,
132 #[serde(default)]
134 pub constructor: StyleConfigEntry,
135 #[serde(default)]
137 pub embedded: StyleConfigEntry,
138 #[serde(default)]
140 pub function_builtin: StyleConfigEntry,
141 #[serde(default)]
143 pub function: StyleConfigEntry,
144 #[serde(default)]
146 pub keyword: StyleConfigEntry,
147 #[serde(default)]
149 pub number: StyleConfigEntry,
150 #[serde(default)]
152 pub module: StyleConfigEntry,
153 #[serde(default)]
155 pub property: StyleConfigEntry,
156 #[serde(default)]
158 pub operator: StyleConfigEntry,
159 #[serde(default)]
161 pub punctuation_bracket: StyleConfigEntry,
162 #[serde(default)]
164 pub punctuation_delimiter: StyleConfigEntry,
165 #[serde(default)]
167 pub string_special: StyleConfigEntry,
168 #[serde(default)]
170 pub string: StyleConfigEntry,
171 #[serde(default)]
173 pub tag: StyleConfigEntry,
174 #[serde(default)]
176 #[serde(rename = "type")]
177 pub type_regular: StyleConfigEntry,
178 #[serde(default)]
180 pub type_builtin: StyleConfigEntry,
181 #[serde(default)]
183 pub variable_builtin: StyleConfigEntry,
184 #[serde(default)]
186 pub variable_parameter: StyleConfigEntry,
187}
188
189#[derive(Default, Debug, Deserialize)]
191pub struct StyleConfigEntry {
192 #[serde(default)]
194 fg: Option<Color>,
195 #[serde(default)]
197 bg: Option<Color>,
198 #[serde(default)]
200 mods: Option<Modifier>,
201}
202
203#[derive(Default, Debug, Deserialize)]
205pub struct SymbolStyleConfigEntry {
206 #[serde(default)]
208 pub symbol: char,
209 #[serde(default)]
211 fg: Option<Color>,
212 #[serde(default)]
214 bg: Option<Color>,
215 #[serde(default)]
217 mods: Option<Modifier>,
218}
219
220impl From<&StyleConfigEntry> for Style {
222 fn from(val: &StyleConfigEntry) -> Self {
224 Style {
225 fg: val.fg,
226 bg: val.bg,
227 underline_color: None,
228 add_modifier: val.mods.unwrap_or(Modifier::empty()),
229 sub_modifier: Modifier::empty(),
230 }
231 }
232}
233
234impl From<&SymbolStyleConfigEntry> for Style {
236 fn from(val: &SymbolStyleConfigEntry) -> Self {
238 Style {
239 fg: val.fg,
240 bg: val.bg,
241 underline_color: None,
242 add_modifier: val.mods.unwrap_or(Modifier::empty()),
243 sub_modifier: Modifier::empty(),
244 }
245 }
246}
247
248pub(crate) fn init_config() -> Res<Config> {
250 let config_path = config_path();
251
252 if config_path.exists() {
253 log::info!("Loading config file at {:?}", config_path);
254 } else {
255 log::info!("No config file at {:?}", config_path);
256 }
257
258 let config = Figment::new()
259 .merge(Toml::string(DEFAULT_CONFIG))
260 .merge(Toml::file(config_path))
261 .extract()
262 .map_err(Error::Config)?;
263
264 Ok(config)
265}
266
267pub fn config_path() -> PathBuf {
269 choose_base_strategy()
270 .expect("Unable to find the config directory!")
271 .config_dir()
272 .join("gitu/config.toml")
273}
274
275#[cfg(test)]
276pub(crate) fn init_test_config() -> Res<Config> {
277 let mut config: Config = Figment::new()
278 .merge(Toml::string(DEFAULT_CONFIG))
279 .extract()
280 .map_err(Error::Config)?;
281
282 config.general.always_show_help.enabled = false;
283 config.general.refresh_on_file_change.enabled = false;
284
285 Ok(config)
286}
287
288#[cfg(test)]
289mod tests {
290 use figment::{
291 providers::{Format, Toml},
292 Figment,
293 };
294 use ratatui::style::Color;
295
296 use super::{Config, DEFAULT_CONFIG};
297
298 #[test]
299 fn config_merges() {
300 let config: Config = Figment::new()
301 .merge(Toml::string(DEFAULT_CONFIG))
302 .merge(Toml::string(
303 r#"
304 [style]
305 hunk_header.bg = "light green"
306 "#,
307 ))
308 .extract()
309 .unwrap();
310
311 assert_eq!(config.style.hunk_header.bg, Some(Color::LightGreen));
312 assert_eq!(config.style.hunk_header.fg, Some(Color::Blue));
313 }
314}