foundry_mcp/core/installation/
paths.rs1use crate::core::installation::utils::{ensure_directory_exists, get_home_dir};
4use anyhow::{Context, Result};
5use std::env;
6use std::path::{Path, PathBuf};
7
8pub fn get_claude_code_config_dir() -> Result<PathBuf> {
13 if let Ok(test_dir) = env::var("CLAUDE_CONFIG_DIR") {
14 return Ok(PathBuf::from(test_dir));
15 }
16 let home = get_home_dir()?;
17 Ok(home.join(".claude"))
18}
19
20pub fn get_claude_code_mcp_config_path() -> Result<PathBuf> {
25 let home = get_home_dir()?;
26 Ok(home.join(".claude.json"))
27}
28
29pub fn get_cursor_config_dir() -> Result<PathBuf> {
34 if let Ok(test_dir) = env::var("CURSOR_CONFIG_DIR") {
35 return Ok(PathBuf::from(test_dir));
36 }
37 let current_dir = std::env::current_dir().context("Failed to get current working directory")?;
38 Ok(current_dir.join(".cursor"))
39}
40
41pub fn get_cursor_mcp_config_path() -> Result<PathBuf> {
43 let config_dir = get_cursor_config_dir()?;
44 ensure_directory_exists(&config_dir)?;
45 Ok(config_dir.join("mcp.json"))
46}
47
48pub fn get_all_config_paths() -> Vec<(String, PathBuf)> {
53 vec![
54 (
55 "claude-code".to_string(),
56 get_claude_code_mcp_config_path().unwrap_or_default(),
57 ),
58 (
59 "cursor".to_string(),
60 get_cursor_mcp_config_path().unwrap_or_default(),
61 ),
62 ]
63}
64
65pub fn validate_config_dir_writable(config_path: &Path) -> Result<()> {
67 let parent_dir = config_path
68 .parent()
69 .context("Configuration path has no parent directory")?;
70
71 ensure_directory_exists(&parent_dir.to_path_buf())?;
73
74 let temp_file = parent_dir.join(".foundry_test_write");
76 match std::fs::write(&temp_file, b"test") {
77 Ok(_) => {
78 let _ = std::fs::remove_file(&temp_file);
80 Ok(())
81 }
82 Err(e) => Err(anyhow::anyhow!(
83 "Configuration directory is not writable: {}. Error: {}",
84 parent_dir.display(),
85 e
86 )),
87 }
88}
89
90pub fn get_platform_info() -> String {
92 format!(
93 "{} {} ({})",
94 env::consts::OS,
95 env::consts::ARCH,
96 env!("CARGO_PKG_VERSION")
97 )
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_get_claude_code_config_dir() {
106 let result = get_claude_code_config_dir();
107 assert!(
108 result.is_ok(),
109 "Should be able to get Claude Code config dir"
110 );
111 let path = result.unwrap();
112 assert!(path.ends_with(".claude"));
113 }
114
115 #[test]
116 fn test_get_claude_code_mcp_config_path() {
117 let result = get_claude_code_mcp_config_path();
118 assert!(
119 result.is_ok(),
120 "Should be able to get Claude Code MCP config path"
121 );
122 let path = result.unwrap();
123 assert!(path.ends_with(".claude.json"));
124 assert!(path.to_string_lossy().contains(".claude"));
125 }
126
127 #[test]
128 fn test_get_cursor_config_dir() {
129 let result = get_cursor_config_dir();
130 assert!(result.is_ok(), "Should be able to get Cursor config dir");
131 let path = result.unwrap();
132 assert!(
133 path.ends_with(".cursor"),
134 "Path should end with '.cursor', got: {}",
135 path.display()
136 );
137 assert!(path.is_absolute(), "Path should be absolute");
138 }
139
140 #[test]
141 fn test_get_cursor_mcp_config_path() {
142 let result = get_cursor_mcp_config_path();
143 assert!(
144 result.is_ok(),
145 "Should be able to get Cursor MCP config path"
146 );
147 let path = result.unwrap();
148 assert!(path.ends_with("mcp.json"));
149 }
150
151 #[test]
152 fn test_get_all_config_paths() {
153 let paths = get_all_config_paths();
154 assert_eq!(paths.len(), 2, "Should return paths for both environments");
155
156 let environment_names: Vec<&String> = paths.iter().map(|(name, _)| name).collect();
157 assert!(environment_names.contains(&&"claude-code".to_string()));
158 assert!(environment_names.contains(&&"cursor".to_string()));
159 }
160
161 #[test]
162 fn test_get_platform_info() {
163 let info = get_platform_info();
164 assert!(!info.is_empty(), "Platform info should not be empty");
165 assert!(
166 info.contains(env::consts::OS),
167 "Platform info should contain OS"
168 );
169 assert!(
170 info.contains(env::consts::ARCH),
171 "Platform info should contain architecture"
172 );
173 }
174}