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}