foundry_mcp/core/installation/
utils.rs1use anyhow::{Context, Result};
4use std::env;
5use std::path::PathBuf;
6
7#[derive(Debug, Clone)]
9pub struct InstallationResult {
10 pub success: bool,
11 pub config_path: String,
12 pub actions_taken: Vec<String>,
13}
14
15#[derive(Debug, Clone)]
17pub struct UninstallationResult {
18 pub success: bool,
19 pub config_path: String,
20 pub actions_taken: Vec<String>,
21 pub files_removed: Vec<String>,
22}
23
24pub fn detect_binary_path() -> Result<String> {
29 let current_exe = env::current_exe().context("Failed to get current executable path")?;
30
31 let binary_path = current_exe
32 .to_str()
33 .context("Binary path contains invalid Unicode characters")?
34 .to_string();
35
36 Ok(binary_path)
37}
38
39pub fn check_binary_accessible(binary_path: &str) -> bool {
41 let path = PathBuf::from(binary_path);
42 path.exists() && path.is_file()
43}
44
45pub fn validate_binary_path(binary_path: &str) -> Result<()> {
47 let path = PathBuf::from(binary_path);
48
49 if !path.exists() {
50 return Err(anyhow::anyhow!(
51 "Binary path does not exist: {}",
52 binary_path
53 ));
54 }
55
56 if !path.is_file() {
57 return Err(anyhow::anyhow!(
58 "Binary path is not a file: {}",
59 binary_path
60 ));
61 }
62
63 Ok(())
64}
65
66pub fn create_installation_result(
68 success: bool,
69 config_path: String,
70 actions_taken: Vec<String>,
71) -> InstallationResult {
72 InstallationResult {
73 success,
74 config_path,
75 actions_taken,
76 }
77}
78
79pub fn create_uninstallation_result(
81 success: bool,
82 config_path: String,
83 actions_taken: Vec<String>,
84 files_removed: Vec<String>,
85) -> UninstallationResult {
86 UninstallationResult {
87 success,
88 config_path,
89 actions_taken,
90 files_removed,
91 }
92}
93
94pub fn file_exists(path: &str) -> bool {
96 PathBuf::from(path).exists()
97}
98
99pub fn read_file_content(path: &str) -> Option<String> {
101 std::fs::read_to_string(path).ok()
102}
103
104pub fn format_actions(actions: &[String]) -> Vec<String> {
106 actions
107 .iter()
108 .map(|action| format!("• {}", action))
109 .collect()
110}
111
112pub fn get_home_dir() -> Result<PathBuf> {
114 dirs::home_dir().context("Failed to determine home directory")
115}
116
117pub fn ensure_directory_exists(dir_path: &PathBuf) -> Result<()> {
119 if !dir_path.exists() {
120 std::fs::create_dir_all(dir_path)
121 .context(format!("Failed to create directory: {:?}", dir_path))?;
122 }
123 Ok(())
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_detect_binary_path() {
132 let result = detect_binary_path();
133 assert!(result.is_ok(), "Should be able to detect binary path");
134 let path = result.unwrap();
135 assert!(!path.is_empty(), "Binary path should not be empty");
136 assert!(
137 path.contains("foundry"),
138 "Binary path should contain 'foundry'"
139 );
140 }
141
142 #[test]
143 fn test_validate_binary_path_valid() {
144 let binary_path = detect_binary_path().unwrap();
145 let result = validate_binary_path(&binary_path);
146 assert!(result.is_ok(), "Valid binary path should pass validation");
147 }
148
149 #[test]
150 fn test_validate_binary_path_invalid() {
151 let result = validate_binary_path("/nonexistent/path");
152 assert!(result.is_err(), "Nonexistent path should fail validation");
153 }
154
155 #[test]
156 fn test_get_home_dir() {
157 let result = get_home_dir();
158 assert!(result.is_ok(), "Should be able to get home directory");
159 let home = result.unwrap();
160 assert!(home.exists(), "Home directory should exist");
161 assert!(home.is_dir(), "Home directory should be a directory");
162 }
163
164 #[test]
165 fn test_create_installation_result() {
166 let result = create_installation_result(
167 true,
168 "/path/to/config".to_string(),
169 vec![
170 "Created config file".to_string(),
171 "Updated environment".to_string(),
172 ],
173 );
174
175 assert!(result.success);
176 assert_eq!(result.config_path, "/path/to/config");
177 assert_eq!(result.actions_taken.len(), 2);
178 }
179
180 #[test]
181 fn test_format_actions() {
182 let actions = vec![
183 "Created config file".to_string(),
184 "Updated environment".to_string(),
185 ];
186
187 let formatted = format_actions(&actions);
188 assert_eq!(formatted.len(), 2);
189 assert!(formatted[0].starts_with("• "));
190 assert!(formatted[1].starts_with("• "));
191 }
192
193 #[test]
194 fn test_format_actions_empty() {
195 let actions: Vec<String> = vec![];
196 let formatted = format_actions(&actions);
197 assert!(formatted.is_empty());
198 }
199
200 #[test]
201 fn test_file_exists() {
202 let temp_dir = tempfile::tempdir().unwrap();
203 let file_path = temp_dir.path().join("test.txt");
204 std::fs::write(&file_path, "test").unwrap();
205
206 assert!(file_exists(file_path.to_str().unwrap()));
207 assert!(!file_exists("/nonexistent/file"));
208 }
209
210 #[test]
211 fn test_read_file_content() {
212 let temp_dir = tempfile::tempdir().unwrap();
213 let file_path = temp_dir.path().join("test.txt");
214 std::fs::write(&file_path, "test content").unwrap();
215
216 let content = read_file_content(file_path.to_str().unwrap());
217 assert_eq!(content, Some("test content".to_string()));
218
219 let nonexistent = read_file_content("/nonexistent/file");
220 assert!(nonexistent.is_none());
221 }
222
223 #[test]
224 fn test_ensure_directory_exists() {
225 let temp_dir = tempfile::tempdir().unwrap();
226 let sub_dir = temp_dir.path().join("subdir").join("nested");
227
228 let result = ensure_directory_exists(&sub_dir);
229 assert!(result.is_ok());
230 assert!(sub_dir.exists());
231 assert!(sub_dir.is_dir());
232 }
233
234 #[test]
235 fn test_create_uninstallation_result() {
236 let result = create_uninstallation_result(
237 false,
238 "/path/to/config.json".to_string(),
239 vec!["Action 1".to_string()],
240 vec!["file1.json".to_string()],
241 );
242
243 assert!(!result.success);
244 assert_eq!(result.config_path, "/path/to/config.json");
245 assert_eq!(result.actions_taken.len(), 1);
246 assert_eq!(result.files_removed.len(), 1);
247 }
248
249 #[test]
250 fn test_check_binary_accessible_valid() {
251 let binary_path = detect_binary_path().unwrap();
252 let accessible = check_binary_accessible(&binary_path);
253 assert!(accessible, "Current binary should be accessible");
254 }
255
256 #[test]
257 fn test_check_binary_accessible_invalid() {
258 let accessible = check_binary_accessible("/nonexistent/path");
259 assert!(!accessible, "Nonexistent path should not be accessible");
260 }
261}