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