1use crate::error::{IdeError, IdeResult};
4use crate::provider_chain::ProviderChainManager;
5use crate::types::*;
6use std::sync::Arc;
7use tracing::{debug, info};
8
9pub struct IdeIntegrationManager {
11 config: IdeIntegrationConfig,
12 provider_chain: Arc<ProviderChainManager>,
13}
14
15impl IdeIntegrationManager {
16 pub fn new(config: IdeIntegrationConfig, provider_chain: Arc<ProviderChainManager>) -> Self {
18 IdeIntegrationManager {
19 config,
20 provider_chain,
21 }
22 }
23
24 pub async fn handle_completion_request(
26 &self,
27 params: &CompletionParams,
28 ) -> IdeResult<Vec<CompletionItem>> {
29 debug!(
30 "Handling completion request for language: {}",
31 params.language
32 );
33
34 if params.language.is_empty() {
36 return Err(IdeError::provider_error(
37 "Language parameter is required for completion request",
38 ));
39 }
40
41 if params.file_path.is_empty() {
42 return Err(IdeError::provider_error(
43 "File path parameter is required for completion request",
44 ));
45 }
46
47 self.provider_chain.get_completions(params).await
49 }
50
51 pub async fn handle_diagnostics_request(
53 &self,
54 params: &DiagnosticsParams,
55 ) -> IdeResult<Vec<Diagnostic>> {
56 debug!(
57 "Handling diagnostics request for language: {}",
58 params.language
59 );
60
61 if params.language.is_empty() {
63 return Err(IdeError::provider_error(
64 "Language parameter is required for diagnostics request",
65 ));
66 }
67
68 if params.file_path.is_empty() {
69 return Err(IdeError::provider_error(
70 "File path parameter is required for diagnostics request",
71 ));
72 }
73
74 self.provider_chain.get_diagnostics(params).await
76 }
77
78 pub async fn handle_hover_request(&self, params: &HoverParams) -> IdeResult<Option<Hover>> {
80 debug!("Handling hover request for language: {}", params.language);
81
82 if params.language.is_empty() {
84 return Err(IdeError::provider_error(
85 "Language parameter is required for hover request",
86 ));
87 }
88
89 if params.file_path.is_empty() {
90 return Err(IdeError::provider_error(
91 "File path parameter is required for hover request",
92 ));
93 }
94
95 self.provider_chain.get_hover(params).await
97 }
98
99 pub async fn handle_definition_request(
101 &self,
102 params: &DefinitionParams,
103 ) -> IdeResult<Option<Location>> {
104 debug!(
105 "Handling definition request for language: {}",
106 params.language
107 );
108
109 if params.language.is_empty() {
111 return Err(IdeError::provider_error(
112 "Language parameter is required for definition request",
113 ));
114 }
115
116 if params.file_path.is_empty() {
117 return Err(IdeError::provider_error(
118 "File path parameter is required for definition request",
119 ));
120 }
121
122 self.provider_chain.get_definition(params).await
124 }
125
126 pub async fn establish_connection(&self, ide_type: &str) -> IdeResult<()> {
128 debug!("Establishing connection with IDE: {}", ide_type);
129
130 match ide_type {
131 "vscode" => {
132 if let Some(vscode_config) = &self.config.vscode {
133 if !vscode_config.enabled {
134 return Err(IdeError::communication_error(
135 "VS Code integration is not enabled in configuration",
136 ));
137 }
138 info!("VS Code connection established on port {}", vscode_config.port);
139 Ok(())
140 } else {
141 Err(IdeError::communication_error(
142 "VS Code configuration not found",
143 ))
144 }
145 }
146 "vim" | "neovim" => {
147 if let Some(terminal_config) = &self.config.terminal {
148 if let Some(vim_config) = &terminal_config.vim {
149 if !vim_config.enabled {
150 return Err(IdeError::communication_error(
151 "Vim/Neovim integration is not enabled in configuration",
152 ));
153 }
154 info!("Vim/Neovim connection established");
155 Ok(())
156 } else {
157 Err(IdeError::communication_error(
158 "Vim/Neovim configuration not found",
159 ))
160 }
161 } else {
162 Err(IdeError::communication_error(
163 "Terminal configuration not found",
164 ))
165 }
166 }
167 "emacs" => {
168 if let Some(terminal_config) = &self.config.terminal {
169 if let Some(emacs_config) = &terminal_config.emacs {
170 if !emacs_config.enabled {
171 return Err(IdeError::communication_error(
172 "Emacs integration is not enabled in configuration",
173 ));
174 }
175 info!("Emacs connection established");
176 Ok(())
177 } else {
178 Err(IdeError::communication_error(
179 "Emacs configuration not found",
180 ))
181 }
182 } else {
183 Err(IdeError::communication_error(
184 "Terminal configuration not found",
185 ))
186 }
187 }
188 _ => Err(IdeError::communication_error(format!(
189 "Unknown IDE type: {}",
190 ide_type
191 ))),
192 }
193 }
194
195 pub async fn close_connection(&self, ide_type: &str) -> IdeResult<()> {
197 debug!("Closing connection with IDE: {}", ide_type);
198 info!("Connection closed for IDE: {}", ide_type);
199 Ok(())
200 }
201
202 pub fn config(&self) -> &IdeIntegrationConfig {
204 &self.config
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use crate::generic_provider::GenericProvider;
212 use crate::provider_chain::ProviderRegistry;
213
214 fn create_test_config() -> IdeIntegrationConfig {
215 IdeIntegrationConfig {
216 vscode: Some(VsCodeConfig {
217 enabled: true,
218 port: 8080,
219 features: vec!["completion".to_string()],
220 settings: serde_json::json!({}),
221 }),
222 terminal: Some(TerminalConfig {
223 vim: Some(VimConfig {
224 enabled: true,
225 plugin_manager: "vim-plug".to_string(),
226 }),
227 emacs: Some(EmacsConfig {
228 enabled: true,
229 package_manager: "use-package".to_string(),
230 }),
231 }),
232 providers: ProviderChainConfig {
233 external_lsp: crate::types::ExternalLspConfig {
234 enabled: true,
235 servers: Default::default(),
236 health_check_interval_ms: 5000,
237 },
238 configured_rules: None,
239 builtin_providers: crate::types::BuiltinProvidersConfig {
240 enabled: true,
241 languages: vec!["rust".to_string()],
242 },
243 },
244 }
245 }
246
247 fn create_test_manager() -> IdeIntegrationManager {
248 let config = create_test_config();
249 let generic_provider = Arc::new(GenericProvider::new());
250 let registry = ProviderRegistry::new(generic_provider);
251 let provider_chain = Arc::new(crate::provider_chain::ProviderChainManager::new(registry));
252 IdeIntegrationManager::new(config, provider_chain)
253 }
254
255 #[tokio::test]
256 async fn test_handle_completion_request_valid() {
257 let manager = create_test_manager();
258
259 let params = CompletionParams {
260 language: "rust".to_string(),
261 file_path: "src/main.rs".to_string(),
262 position: Position {
263 line: 10,
264 character: 5,
265 },
266 context: "fn test".to_string(),
267 };
268
269 let result = manager.handle_completion_request(¶ms).await;
270 assert!(result.is_ok());
271 }
272
273 #[tokio::test]
274 async fn test_handle_completion_request_empty_language() {
275 let manager = create_test_manager();
276
277 let params = CompletionParams {
278 language: "".to_string(),
279 file_path: "src/main.rs".to_string(),
280 position: Position {
281 line: 10,
282 character: 5,
283 },
284 context: "fn test".to_string(),
285 };
286
287 let result = manager.handle_completion_request(¶ms).await;
288 assert!(result.is_err());
289 }
290
291 #[tokio::test]
292 async fn test_handle_completion_request_empty_file_path() {
293 let manager = create_test_manager();
294
295 let params = CompletionParams {
296 language: "rust".to_string(),
297 file_path: "".to_string(),
298 position: Position {
299 line: 10,
300 character: 5,
301 },
302 context: "fn test".to_string(),
303 };
304
305 let result = manager.handle_completion_request(¶ms).await;
306 assert!(result.is_err());
307 }
308
309 #[tokio::test]
310 async fn test_handle_diagnostics_request_valid() {
311 let manager = create_test_manager();
312
313 let params = DiagnosticsParams {
314 language: "rust".to_string(),
315 file_path: "src/main.rs".to_string(),
316 source: "fn test() {}".to_string(),
317 };
318
319 let result = manager.handle_diagnostics_request(¶ms).await;
320 assert!(result.is_ok());
321 }
322
323 #[tokio::test]
324 async fn test_handle_hover_request_valid() {
325 let manager = create_test_manager();
326
327 let params = HoverParams {
328 language: "rust".to_string(),
329 file_path: "src/main.rs".to_string(),
330 position: Position {
331 line: 10,
332 character: 5,
333 },
334 };
335
336 let result = manager.handle_hover_request(¶ms).await;
337 assert!(result.is_ok());
338 }
339
340 #[tokio::test]
341 async fn test_handle_definition_request_valid() {
342 let manager = create_test_manager();
343
344 let params = DefinitionParams {
345 language: "rust".to_string(),
346 file_path: "src/main.rs".to_string(),
347 position: Position {
348 line: 10,
349 character: 5,
350 },
351 };
352
353 let result = manager.handle_definition_request(¶ms).await;
354 assert!(result.is_ok());
355 }
356
357 #[tokio::test]
358 async fn test_establish_vscode_connection() {
359 let manager = create_test_manager();
360
361 let result = manager.establish_connection("vscode").await;
362 assert!(result.is_ok());
363 }
364
365 #[tokio::test]
366 async fn test_establish_vim_connection() {
367 let manager = create_test_manager();
368
369 let result = manager.establish_connection("vim").await;
370 assert!(result.is_ok());
371 }
372
373 #[tokio::test]
374 async fn test_establish_emacs_connection() {
375 let manager = create_test_manager();
376
377 let result = manager.establish_connection("emacs").await;
378 assert!(result.is_ok());
379 }
380
381 #[tokio::test]
382 async fn test_establish_unknown_ide_connection() {
383 let manager = create_test_manager();
384
385 let result = manager.establish_connection("unknown").await;
386 assert!(result.is_err());
387 }
388
389 #[tokio::test]
390 async fn test_close_connection() {
391 let manager = create_test_manager();
392
393 let result = manager.close_connection("vscode").await;
394 assert!(result.is_ok());
395 }
396}