ricecoder_lsp/diagnostics/
mod.rs1pub mod adapters;
51pub mod generic_engine;
52pub mod python_rules;
53pub mod rust_rules;
54pub mod typescript_rules;
55
56pub use adapters::{
57 PythonDiagnosticsAdapter, RustDiagnosticsAdapter, TypeScriptDiagnosticsAdapter,
58};
59pub use generic_engine::GenericDiagnosticsEngine;
60
61use crate::types::{Diagnostic, Language, Range};
62use std::error::Error;
63use std::fmt;
64
65#[derive(Debug, Clone)]
67pub enum DiagnosticsError {
68 AnalysisFailed(String),
70 InvalidInput(String),
72 UnsupportedLanguage(String),
74}
75
76impl fmt::Display for DiagnosticsError {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 match self {
79 DiagnosticsError::AnalysisFailed(msg) => write!(f, "Analysis failed: {}", msg),
80 DiagnosticsError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
81 DiagnosticsError::UnsupportedLanguage(lang) => {
82 write!(f, "Unsupported language: {}", lang)
83 }
84 }
85 }
86}
87
88impl Error for DiagnosticsError {}
89
90pub type DiagnosticsResult<T> = Result<T, DiagnosticsError>;
92
93pub trait DiagnosticsEngine: Send + Sync {
95 fn generate_diagnostics(
97 &self,
98 code: &str,
99 language: Language,
100 ) -> DiagnosticsResult<Vec<Diagnostic>>;
101
102 fn generate_diagnostics_for_range(
104 &self,
105 code: &str,
106 language: Language,
107 range: Range,
108 ) -> DiagnosticsResult<Vec<Diagnostic>>;
109}
110
111pub struct DefaultDiagnosticsEngine;
113
114impl DefaultDiagnosticsEngine {
115 pub fn new() -> Self {
117 Self
118 }
119}
120
121impl Default for DefaultDiagnosticsEngine {
122 fn default() -> Self {
123 Self::new()
124 }
125}
126
127impl DiagnosticsEngine for DefaultDiagnosticsEngine {
128 fn generate_diagnostics(
129 &self,
130 code: &str,
131 language: Language,
132 ) -> DiagnosticsResult<Vec<Diagnostic>> {
133 if code.is_empty() {
134 return Ok(Vec::new());
135 }
136
137 match language {
138 Language::Rust => rust_rules::generate_rust_diagnostics(code),
139 Language::TypeScript => typescript_rules::generate_typescript_diagnostics(code),
140 Language::Python => python_rules::generate_python_diagnostics(code),
141 Language::Unknown => {
142 Ok(Vec::new())
144 }
145 }
146 }
147
148 fn generate_diagnostics_for_range(
149 &self,
150 code: &str,
151 language: Language,
152 range: Range,
153 ) -> DiagnosticsResult<Vec<Diagnostic>> {
154 let all_diagnostics = self.generate_diagnostics(code, language)?;
155
156 let filtered = all_diagnostics
158 .into_iter()
159 .filter(|diag| {
160 diag.range.start.line >= range.start.line && diag.range.end.line <= range.end.line
161 })
162 .collect();
163
164 Ok(filtered)
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_diagnostics_engine_empty_code() {
174 let engine = DefaultDiagnosticsEngine::new();
175 let result = engine.generate_diagnostics("", Language::Rust);
176 assert!(result.is_ok());
177 assert!(result.unwrap().is_empty());
178 }
179
180 #[test]
181 fn test_diagnostics_engine_unknown_language() {
182 let engine = DefaultDiagnosticsEngine::new();
183 let result = engine.generate_diagnostics("some code", Language::Unknown);
184 assert!(result.is_ok());
185 assert!(result.unwrap().is_empty());
186 }
187
188 #[test]
189 fn test_diagnostics_error_display() {
190 let err = DiagnosticsError::AnalysisFailed("test error".to_string());
191 assert_eq!(err.to_string(), "Analysis failed: test error");
192 }
193}