pars_core/config/
cli.rs

1use std::error::Error;
2use std::fs;
3use std::path::Path;
4#[allow(dead_code)]
5use std::{env, path};
6
7use serde::{Deserialize, Serialize};
8
9use crate::constants::default_constants::{EDITOR, GIT_EXECUTABLE, PGP_EXECUTABLE};
10
11#[derive(Debug, Serialize, Deserialize, Default, Eq, PartialEq)]
12#[serde(default)]
13pub struct ParsConfig {
14    #[serde(default = "PrintConfig::default")]
15    pub print_config: PrintConfig,
16    #[serde(default = "PathConfig::default")]
17    pub path_config: PathConfig,
18    #[serde(default = "ExecutableConfig::default")]
19    pub executable_config: ExecutableConfig,
20    #[serde(default = "ClipConfig::default")]
21    pub clip_config: ClipConfig,
22}
23
24#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
25pub struct PrintConfig {
26    pub dir_color: String,
27    pub file_color: String,
28    pub symbol_color: String,
29    pub tree_color: String,
30    pub grep_pass_color: String,
31    pub grep_match_color: String,
32}
33
34#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
35pub struct PathConfig {
36    pub default_repo: String,
37    pub repos: Vec<String>,
38}
39
40#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
41pub struct ExecutableConfig {
42    pub pgp_executable: String,
43    pub editor_executable: String,
44    pub git_executable: String,
45}
46
47#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
48pub struct ClipConfig {
49    pub clip_time: Option<usize>,
50}
51
52impl Default for PrintConfig {
53    fn default() -> Self {
54        Self {
55            dir_color: "cyan".into(),
56            file_color: String::new(),
57            symbol_color: "bright green".into(),
58            tree_color: String::new(),
59            grep_pass_color: "bright green".into(),
60            grep_match_color: "bright red".into(),
61        }
62    }
63}
64
65impl AsRef<PrintConfig> for PrintConfig {
66    fn as_ref(&self) -> &PrintConfig {
67        self
68    }
69}
70
71impl PrintConfig {
72    pub fn none() -> Self {
73        Self {
74            dir_color: String::new(),
75            file_color: String::new(),
76            symbol_color: String::new(),
77            tree_color: String::new(),
78            grep_pass_color: String::new(),
79            grep_match_color: String::new(),
80        }
81    }
82}
83
84impl Default for ExecutableConfig {
85    fn default() -> Self {
86        Self {
87            pgp_executable: PGP_EXECUTABLE.into(),
88            editor_executable: EDITOR.into(),
89            git_executable: GIT_EXECUTABLE.into(),
90        }
91    }
92}
93
94impl Default for PathConfig {
95    fn default() -> Self {
96        let default_path = match dirs::home_dir() {
97            Some(path) => {
98                format!("{}{}.password-store", path.display(), path::MAIN_SEPARATOR)
99            }
100            None => {
101                format!(
102                    "{}{}.password-store",
103                    env::var(
104                        #[cfg(unix)]
105                        {
106                            "HOME"
107                        },
108                        #[cfg(windows)]
109                        {
110                            "USERPROFILE"
111                        }
112                    )
113                    .unwrap_or("~".into()),
114                    path::MAIN_SEPARATOR
115                )
116            }
117        };
118        PathConfig { default_repo: default_path.clone(), repos: vec![default_path] }
119    }
120}
121
122impl Default for ClipConfig {
123    fn default() -> Self {
124        ClipConfig { clip_time: Some(45) }
125    }
126}
127
128pub fn load_config<P: AsRef<Path>>(path: P) -> Result<ParsConfig, Box<dyn Error>> {
129    let content = fs::read_to_string(path)?;
130    let config: ParsConfig = toml::from_str(&content)?;
131    Ok(config)
132}
133
134pub fn save_config<P: AsRef<Path>>(config: &ParsConfig, path: P) -> Result<(), Box<dyn Error>> {
135    let toml_str = toml::to_string_pretty(config)?;
136    fs::write(path, toml_str)?;
137    Ok(())
138}
139
140#[cfg(test)]
141mod tests {
142    use pretty_assertions::assert_eq;
143
144    use super::*;
145    use crate::util::test_util::gen_unique_temp_dir;
146
147    #[test]
148    fn load_save_test() {
149        let (_temp_dir, root) = gen_unique_temp_dir();
150        let config_path = root.join("config.toml");
151
152        let test_config = ParsConfig::default();
153        save_config(&test_config, &config_path).unwrap();
154        let loaded_config = load_config(&config_path).unwrap();
155        assert_eq!(test_config, loaded_config);
156    }
157
158    #[test]
159    fn generate_default_config_test() {
160        let mut default_config = ParsConfig::default();
161        default_config.path_config.default_repo = "<Your Home>/.password-store".into();
162        default_config.path_config.repos = vec!["<Your Home>/.password-store".into()];
163        let root = env!("CARGO_MANIFEST_DIR");
164        let save_path = Path::new(root).parent().unwrap().join("config").join("cli");
165        if !save_path.exists() {
166            fs::create_dir_all(&save_path).unwrap();
167        }
168        save_config(&default_config, save_path.join("pars_config.toml"))
169            .expect("Failed to save default config");
170    }
171
172    #[test]
173    fn invalid_path_test() {
174        let test_config = ParsConfig::default();
175        let result = if cfg!(unix) {
176            save_config(&test_config, "/home/user/\0file.txt")
177        } else if cfg!(windows) {
178            save_config(&test_config, "C:\\<illegal>\\invalid.toml")
179        } else {
180            Err(Box::from("Unsupported OS"))
181        };
182
183        assert!(result.is_err());
184    }
185}