1use crate::tui::Theme;
2use crate::utils::expand_path;
3use serde::Deserialize;
4use serde::Serialize;
5use std::fs;
6use std::io::Write;
7use std::path::{Path, PathBuf};
8
9#[derive(Deserialize, Serialize)]
10pub struct Config {
11 pub tries_paths: Option<String>,
12 pub tries_path: Option<String>,
13 pub theme: Option<String>,
14 pub editor: Option<String>,
15 pub apply_date_prefix: Option<bool>,
16 pub transparent_background: Option<bool>,
17 pub show_disk: Option<bool>,
18 pub show_preview: Option<bool>,
19 pub show_legend: Option<bool>,
20 pub show_right_panel: Option<bool>,
21 pub right_panel_width: Option<u16>,
22}
23
24pub fn get_file_config_toml_name() -> String {
25 std::env::var("TRY_CONFIG").unwrap_or("config.toml".to_string())
26}
27
28pub fn get_config_dir() -> PathBuf {
29 std::env::var_os("TRY_CONFIG_DIR")
30 .map(PathBuf::from)
31 .unwrap_or_else(|| get_base_config_dir().join("try-rs"))
32}
33
34pub fn get_base_config_dir() -> PathBuf {
39 std::env::var_os("XDG_CONFIG_HOME")
40 .map(PathBuf::from)
41 .or_else(dirs::config_dir)
42 .unwrap_or_else(|| {
43 dirs::home_dir()
44 .expect("Could not find home directory")
45 .join(".config")
46 })
47}
48
49fn config_candidates() -> Vec<PathBuf> {
51 let config_name = get_file_config_toml_name();
52 let mut candidates = Vec::new();
53
54 if let Some(env_dir) = std::env::var_os("TRY_CONFIG_DIR") {
55 candidates.push(PathBuf::from(env_dir).join(&config_name));
56 }
57 let base_dir = get_base_config_dir();
58 candidates.push(base_dir.join("try-rs").join(&config_name));
59 if let Some(home) = dirs::home_dir() {
60 candidates.push(home.join(".config").join("try-rs").join(&config_name));
61 }
62
63 candidates
64}
65
66fn find_config_path() -> Option<PathBuf> {
68 config_candidates().into_iter().find(|p| p.exists())
69}
70
71pub fn load_file_config_toml_if_exists() -> Option<Config> {
72 let path = find_config_path()?;
73 let contents = fs::read_to_string(&path).ok()?;
74 toml::from_str::<Config>(&contents).ok()
75}
76
77pub struct AppConfig {
78 pub tries_dirs: Vec<PathBuf>,
79 pub active_tab: usize,
80 pub theme: Theme,
81 pub editor_cmd: Option<String>,
82 pub config_path: Option<PathBuf>,
83 pub apply_date_prefix: Option<bool>,
84 pub transparent_background: Option<bool>,
85 pub show_disk: Option<bool>,
86 pub show_preview: Option<bool>,
87 pub show_legend: Option<bool>,
88 pub show_right_panel: Option<bool>,
89 pub right_panel_width: Option<u16>,
90}
91
92pub fn load_configuration() -> AppConfig {
93 let default_path = dirs::home_dir()
94 .expect("Folder not found")
95 .join("work")
96 .join("tries");
97
98 let mut theme = Theme::default();
99 let try_path = std::env::var_os("TRY_PATH");
100 let try_path_specified = try_path.is_some();
101 let mut final_paths: Vec<PathBuf> = if let Some(path) = try_path {
102 vec![path.into()]
103 } else {
104 vec![default_path]
105 };
106 let mut editor_cmd = std::env::var("VISUAL")
107 .ok()
108 .or_else(|| std::env::var("EDITOR").ok());
109 let mut apply_date_prefix = None;
110 let mut transparent_background = None;
111 let mut show_disk = None;
112 let mut show_preview = None;
113 let mut show_legend = None;
114 let mut show_right_panel = None;
115 let mut right_panel_width = None;
116
117 let loaded_config_path = find_config_path();
118
119 if let Some(config) = load_file_config_toml_if_exists() {
120 let paths_source = config.tries_paths.or(config.tries_path);
121
122 if let Some(paths_str) = paths_source
123 && !try_path_specified
124 {
125 final_paths = paths_str
126 .split(',')
127 .map(|s| s.trim())
128 .filter(|s| !s.is_empty())
129 .map(expand_path)
130 .collect();
131 }
132 if let Some(editor) = config.editor {
133 editor_cmd = Some(editor);
134 }
135 if let Some(theme_name) = config.theme {
136 if let Some(found_theme) = Theme::all().into_iter().find(|t| t.name == theme_name) {
137 theme = found_theme;
138 }
139 }
140 apply_date_prefix = config.apply_date_prefix;
141 transparent_background = config.transparent_background;
142 show_disk = config.show_disk;
143 show_preview = config.show_preview;
144 show_legend = config.show_legend;
145 show_right_panel = config.show_right_panel;
146 right_panel_width = config.right_panel_width;
147 }
148
149 AppConfig {
150 tries_dirs: final_paths,
151 active_tab: 0,
152 theme,
153 editor_cmd,
154 config_path: loaded_config_path,
155 apply_date_prefix,
156 transparent_background,
157 show_disk,
158 show_preview,
159 show_legend,
160 show_right_panel,
161 right_panel_width,
162 }
163}
164
165pub fn save_config(
166 path: &Path,
167 theme: &Theme,
168 tries_paths: &[PathBuf],
169 editor: &Option<String>,
170 apply_date_prefix: Option<bool>,
171 transparent_background: Option<bool>,
172 show_disk: Option<bool>,
173 show_preview: Option<bool>,
174 show_legend: Option<bool>,
175 show_right_panel: Option<bool>,
176 right_panel_width: Option<u16>,
177) -> std::io::Result<()> {
178 let paths_string = tries_paths
179 .iter()
180 .map(|p| p.to_string_lossy().to_string())
181 .collect::<Vec<_>>()
182 .join(", ");
183
184 let config = Config {
185 tries_paths: Some(paths_string),
186 tries_path: tries_paths.first().map(|p| p.to_string_lossy().to_string()),
187 theme: Some(theme.name.clone()),
188 editor: editor.clone(),
189 apply_date_prefix,
190 transparent_background,
191 show_disk,
192 show_preview,
193 show_legend,
194 show_right_panel,
195 right_panel_width,
196 };
197
198 let toml_string =
199 toml::to_string(&config).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
200
201 if let Some(parent) = path.parent() {
202 if !parent.exists() {
203 fs::create_dir_all(parent)?;
204 }
205 }
206
207 let mut file = fs::File::create(path)?;
208 file.write_all(toml_string.as_bytes())?;
209 Ok(())
210}