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");
13
14#[derive(Default, Debug, Deserialize)]
15pub(crate) struct Config {
16 pub general: GeneralConfig,
17 pub style: StyleConfig,
18 pub bindings: BTreeMap<Menu, BTreeMap<Op, Vec<String>>>,
19}
20
21#[derive(Default, Debug, Deserialize)]
22pub struct GeneralConfig {
23 pub always_show_help: BoolConfigEntry,
24 pub confirm_quit: BoolConfigEntry,
25 pub refresh_on_file_change: BoolConfigEntry,
26 pub collapsed_sections: Vec<String>,
27}
28
29#[derive(Default, Debug, Deserialize)]
30pub struct BoolConfigEntry {
31 #[serde(default)]
32 pub enabled: bool,
33}
34
35#[derive(Default, Debug, Deserialize)]
36pub struct StyleConfig {
37 pub section_header: StyleConfigEntry,
38 pub file_header: StyleConfigEntry,
39 pub hunk_header: StyleConfigEntry,
40
41 #[serde(default)]
42 pub diff_highlight: DiffHighlightConfig,
43
44 #[serde(default)]
45 pub syntax_highlight: SyntaxHighlightConfig,
46
47 pub cursor: SymbolStyleConfigEntry,
48 pub selection_line: StyleConfigEntry,
49 pub selection_bar: SymbolStyleConfigEntry,
50 pub selection_area: StyleConfigEntry,
51
52 pub hash: StyleConfigEntry,
53 pub branch: StyleConfigEntry,
54 pub remote: StyleConfigEntry,
55 pub tag: StyleConfigEntry,
56
57 pub command: StyleConfigEntry,
58 pub active_arg: StyleConfigEntry,
59 pub hotkey: StyleConfigEntry,
60}
61
62#[derive(Default, Debug, Deserialize)]
63pub struct DiffHighlightConfig {
64 #[serde(default)]
65 pub tag_old: StyleConfigEntry,
66 #[serde(default)]
67 pub tag_new: StyleConfigEntry,
68 #[serde(default)]
69 pub unchanged_old: StyleConfigEntry,
70 #[serde(default)]
71 pub unchanged_new: StyleConfigEntry,
72 #[serde(default)]
73 pub changed_old: StyleConfigEntry,
74 #[serde(default)]
75 pub changed_new: StyleConfigEntry,
76}
77
78#[derive(Default, Debug, Deserialize)]
79pub struct SyntaxHighlightConfig {
80 #[serde(default)]
81 pub enabled: bool,
82 #[serde(default)]
83 pub attribute: StyleConfigEntry,
84 #[serde(default)]
85 pub comment: StyleConfigEntry,
86 #[serde(default)]
87 pub constant_builtin: StyleConfigEntry,
88 #[serde(default)]
89 pub constant: StyleConfigEntry,
90 #[serde(default)]
91 pub constructor: StyleConfigEntry,
92 #[serde(default)]
93 pub embedded: StyleConfigEntry,
94 #[serde(default)]
95 pub function_builtin: StyleConfigEntry,
96 #[serde(default)]
97 pub function: StyleConfigEntry,
98 #[serde(default)]
99 pub keyword: StyleConfigEntry,
100 #[serde(default)]
101 pub number: StyleConfigEntry,
102 #[serde(default)]
103 pub module: StyleConfigEntry,
104 #[serde(default)]
105 pub property: StyleConfigEntry,
106 #[serde(default)]
107 pub operator: StyleConfigEntry,
108 #[serde(default)]
109 pub punctuation_bracket: StyleConfigEntry,
110 #[serde(default)]
111 pub punctuation_delimiter: StyleConfigEntry,
112 #[serde(default)]
113 pub string_special: StyleConfigEntry,
114 #[serde(default)]
115 pub string: StyleConfigEntry,
116 #[serde(default)]
117 pub tag: StyleConfigEntry,
118 #[serde(default)]
119 #[serde(rename = "type")]
120 pub type_regular: StyleConfigEntry,
121 #[serde(default)]
122 pub type_builtin: StyleConfigEntry,
123 #[serde(default)]
124 pub variable_builtin: StyleConfigEntry,
125 #[serde(default)]
126 pub variable_parameter: StyleConfigEntry,
127}
128
129#[derive(Default, Debug, Deserialize)]
130pub struct StyleConfigEntry {
131 #[serde(default)]
132 fg: Option<Color>,
133 #[serde(default)]
134 bg: Option<Color>,
135 #[serde(default)]
136 mods: Option<Modifier>,
137}
138
139#[derive(Default, Debug, Deserialize)]
140pub struct SymbolStyleConfigEntry {
141 #[serde(default)]
142 pub symbol: char,
143 #[serde(default)]
144 fg: Option<Color>,
145 #[serde(default)]
146 bg: Option<Color>,
147 #[serde(default)]
148 mods: Option<Modifier>,
149}
150
151impl From<&StyleConfigEntry> for Style {
152 fn from(val: &StyleConfigEntry) -> Self {
153 Style {
154 fg: val.fg,
155 bg: val.bg,
156 underline_color: None,
157 add_modifier: val.mods.unwrap_or(Modifier::empty()),
158 sub_modifier: Modifier::empty(),
159 }
160 }
161}
162
163impl From<&SymbolStyleConfigEntry> for Style {
164 fn from(val: &SymbolStyleConfigEntry) -> Self {
165 Style {
166 fg: val.fg,
167 bg: val.bg,
168 underline_color: None,
169 add_modifier: val.mods.unwrap_or(Modifier::empty()),
170 sub_modifier: Modifier::empty(),
171 }
172 }
173}
174
175pub(crate) fn init_config() -> Res<Config> {
176 let config_path = config_path();
177
178 if config_path.exists() {
179 log::info!("Loading config file at {:?}", config_path);
180 } else {
181 log::info!("No config file at {:?}", config_path);
182 }
183
184 let config = Figment::new()
185 .merge(Toml::string(DEFAULT_CONFIG))
186 .merge(Toml::file(config_path))
187 .extract()
188 .map_err(Error::Config)?;
189
190 Ok(config)
191}
192
193pub fn config_path() -> PathBuf {
194 choose_base_strategy()
195 .expect("Unable to find the config directory!")
196 .config_dir()
197 .join("gitu/config.toml")
198}
199
200#[cfg(test)]
201pub(crate) fn init_test_config() -> Res<Config> {
202 let mut config: Config = Figment::new()
203 .merge(Toml::string(DEFAULT_CONFIG))
204 .extract()
205 .map_err(Error::Config)?;
206
207 config.general.always_show_help.enabled = false;
208 config.general.refresh_on_file_change.enabled = false;
209
210 Ok(config)
211}
212
213#[cfg(test)]
214mod tests {
215 use figment::{
216 providers::{Format, Toml},
217 Figment,
218 };
219 use ratatui::style::Color;
220
221 use super::{Config, DEFAULT_CONFIG};
222
223 #[test]
224 fn config_merges() {
225 let config: Config = Figment::new()
226 .merge(Toml::string(DEFAULT_CONFIG))
227 .merge(Toml::string(
228 r#"
229 [style]
230 hunk_header.bg = "light green"
231 "#,
232 ))
233 .extract()
234 .unwrap();
235
236 assert_eq!(config.style.hunk_header.bg, Some(Color::LightGreen));
237 assert_eq!(config.style.hunk_header.fg, Some(Color::Blue));
238 }
239}