ricecoder_ide/
manager.rs

1//! IDE Integration Manager
2
3use crate::error::{IdeError, IdeResult};
4use crate::provider_chain::ProviderChainManager;
5use crate::types::*;
6use std::sync::Arc;
7use tracing::{debug, info};
8
9/// IDE Integration Manager
10pub struct IdeIntegrationManager {
11    config: IdeIntegrationConfig,
12    provider_chain: Arc<ProviderChainManager>,
13}
14
15impl IdeIntegrationManager {
16    /// Create a new IDE Integration Manager
17    pub fn new(config: IdeIntegrationConfig, provider_chain: Arc<ProviderChainManager>) -> Self {
18        IdeIntegrationManager {
19            config,
20            provider_chain,
21        }
22    }
23
24    /// Handle completion request
25    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        // Validate parameters
35        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        // Route through provider chain
48        self.provider_chain.get_completions(params).await
49    }
50
51    /// Handle diagnostics request
52    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        // Validate parameters
62        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        // Route through provider chain
75        self.provider_chain.get_diagnostics(params).await
76    }
77
78    /// Handle hover request
79    pub async fn handle_hover_request(&self, params: &HoverParams) -> IdeResult<Option<Hover>> {
80        debug!("Handling hover request for language: {}", params.language);
81
82        // Validate parameters
83        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        // Route through provider chain
96        self.provider_chain.get_hover(params).await
97    }
98
99    /// Handle definition request
100    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        // Validate parameters
110        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        // Route through provider chain
123        self.provider_chain.get_definition(params).await
124    }
125
126    /// Establish connection with IDE
127    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    /// Close connection with IDE
196    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    /// Get configuration
203    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(&params).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(&params).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(&params).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(&params).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(&params).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(&params).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}