Skip to main content

try_rs/
config.rs

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_path: Option<String>,
12    pub theme: Option<String>,
13    pub editor: Option<String>,
14    pub apply_date_prefix: Option<bool>,
15    pub transparent_background: Option<bool>,
16    pub show_disk: Option<bool>,
17    pub show_preview: Option<bool>,
18    pub show_legend: Option<bool>,
19    pub show_right_panel: Option<bool>,
20    pub right_panel_width: Option<u16>,
21}
22
23pub fn get_file_config_toml_name() -> String {
24    std::env::var("TRY_CONFIG").unwrap_or("config.toml".to_string())
25}
26
27pub fn get_config_dir() -> PathBuf {
28    std::env::var_os("TRY_CONFIG_DIR")
29        .map(PathBuf::from)
30        .unwrap_or_else(|| get_base_config_dir().join("try-rs"))
31}
32
33pub fn get_base_config_dir() -> PathBuf {
34    dirs::config_dir().unwrap_or_else(|| {
35        dirs::home_dir()
36            .expect("Could not find home directory")
37            .join(".config")
38    })
39}
40
41/// Returns candidate config file paths in priority order.
42fn config_candidates() -> Vec<PathBuf> {
43    let config_name = get_file_config_toml_name();
44    let mut candidates = Vec::new();
45
46    if let Some(env_dir) = std::env::var_os("TRY_CONFIG_DIR") {
47        candidates.push(PathBuf::from(env_dir).join(&config_name));
48    }
49    if let Some(dir) = dirs::config_dir() {
50        candidates.push(dir.join("try-rs").join(&config_name));
51    }
52    if let Some(home) = dirs::home_dir() {
53        candidates.push(home.join(".config").join("try-rs").join(&config_name));
54    }
55
56    candidates
57}
58
59/// Finds the first existing config file path.
60fn find_config_path() -> Option<PathBuf> {
61    config_candidates().into_iter().find(|p| p.exists())
62}
63
64pub fn load_file_config_toml_if_exists() -> Option<Config> {
65    let path = find_config_path()?;
66    let contents = fs::read_to_string(&path).ok()?;
67    toml::from_str::<Config>(&contents).ok()
68}
69
70pub struct AppConfig {
71    pub tries_dir: PathBuf,
72    pub theme: Theme,
73    pub editor_cmd: Option<String>,
74    pub config_path: Option<PathBuf>,
75    pub apply_date_prefix: Option<bool>,
76    pub transparent_background: Option<bool>,
77    pub show_disk: Option<bool>,
78    pub show_preview: Option<bool>,
79    pub show_legend: Option<bool>,
80    pub show_right_panel: Option<bool>,
81    pub right_panel_width: Option<u16>,
82}
83
84pub fn load_configuration() -> AppConfig {
85    let default_path = dirs::home_dir()
86        .expect("Folder not found")
87        .join("work")
88        .join("tries");
89
90    let mut theme = Theme::default();
91    let try_path = std::env::var_os("TRY_PATH");
92    let try_path_specified = try_path.is_some();
93    let mut final_path = try_path.map(PathBuf::from).unwrap_or(default_path);
94    let mut editor_cmd = std::env::var("VISUAL")
95        .ok()
96        .or_else(|| std::env::var("EDITOR").ok());
97    let mut apply_date_prefix = None;
98    let mut transparent_background = None;
99    let mut show_disk = None;
100    let mut show_preview = None;
101    let mut show_legend = None;
102    let mut show_right_panel = None;
103    let mut right_panel_width = None;
104
105    let loaded_config_path = find_config_path();
106
107    if let Some(config) = load_file_config_toml_if_exists() {
108        if let Some(path_str) = config.tries_path
109            && !try_path_specified
110        {
111            final_path = expand_path(&path_str);
112        }
113        if let Some(editor) = config.editor {
114            editor_cmd = Some(editor);
115        }
116        if let Some(theme_name) = config.theme {
117            if let Some(found_theme) = Theme::all().into_iter().find(|t| t.name == theme_name) {
118                theme = found_theme;
119            }
120        }
121        apply_date_prefix = config.apply_date_prefix;
122        transparent_background = config.transparent_background;
123        show_disk = config.show_disk;
124        show_preview = config.show_preview;
125        show_legend = config.show_legend;
126        show_right_panel = config.show_right_panel;
127        right_panel_width = config.right_panel_width;
128    }
129
130    AppConfig {
131        tries_dir: final_path,
132        theme,
133        editor_cmd,
134        config_path: loaded_config_path,
135        apply_date_prefix,
136        transparent_background,
137        show_disk,
138        show_preview,
139        show_legend,
140        show_right_panel,
141        right_panel_width,
142    }
143}
144
145pub fn save_config(
146    path: &Path,
147    theme: &Theme,
148    tries_path: &Path,
149    editor: &Option<String>,
150    apply_date_prefix: Option<bool>,
151    transparent_background: Option<bool>,
152    show_disk: Option<bool>,
153    show_preview: Option<bool>,
154    show_legend: Option<bool>,
155    show_right_panel: Option<bool>,
156    right_panel_width: Option<u16>,
157) -> std::io::Result<()> {
158    let config = Config {
159        tries_path: Some(tries_path.to_string_lossy().to_string()),
160        theme: Some(theme.name.clone()),
161        editor: editor.clone(),
162        apply_date_prefix,
163        transparent_background,
164        show_disk,
165        show_preview,
166        show_legend,
167        show_right_panel,
168        right_panel_width,
169    };
170
171    let toml_string =
172        toml::to_string(&config).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
173
174    if let Some(parent) = path.parent() {
175        if !parent.exists() {
176            fs::create_dir_all(parent)?;
177        }
178    }
179
180    let mut file = fs::File::create(path)?;
181    file.write_all(toml_string.as_bytes())?;
182    Ok(())
183}