deps_lsp/handlers/
diagnostics.rs1use crate::config::{DepsConfig, DiagnosticsConfig};
4use crate::document::{ServerState, ensure_document_loaded};
5use std::sync::Arc;
6use tokio::sync::RwLock;
7use tower_lsp_server::Client;
8use tower_lsp_server::ls_types::{Diagnostic, Uri};
9
10pub async fn handle_diagnostics(
12 state: Arc<ServerState>,
13 uri: &Uri,
14 _config: &DiagnosticsConfig,
15 client: Client,
16 full_config: Arc<RwLock<DepsConfig>>,
17) -> Vec<Diagnostic> {
18 if !ensure_document_loaded(uri, Arc::clone(&state), client, full_config).await {
20 tracing::warn!("Could not load document for diagnostics: {:?}", uri);
21 return vec![];
22 }
23
24 generate_diagnostics_internal(state, uri).await
25}
26
27pub(crate) async fn generate_diagnostics_internal(
31 state: Arc<ServerState>,
32 uri: &Uri,
33) -> Vec<Diagnostic> {
34 let doc = match state.get_document(uri) {
36 Some(d) => d,
37 None => {
38 tracing::warn!("Document not found for diagnostics: {:?}", uri);
39 return vec![];
40 }
41 };
42
43 let ecosystem = match state.ecosystem_registry.get(doc.ecosystem_id) {
44 Some(e) => e,
45 None => {
46 tracing::warn!("Ecosystem not found for diagnostics: {}", doc.ecosystem_id);
47 return vec![];
48 }
49 };
50
51 let parse_result = match doc.parse_result() {
52 Some(p) => p,
53 None => return vec![],
54 };
55
56 ecosystem
58 .generate_diagnostics(
59 parse_result,
60 &doc.cached_versions,
61 &doc.resolved_versions,
62 uri,
63 )
64 .await
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::config::DiagnosticsConfig;
71 use crate::document::ServerState;
72 use crate::test_utils::test_helpers::create_test_client_and_config;
73
74 #[tokio::test]
77 async fn test_handle_diagnostics_missing_document() {
78 let state = Arc::new(ServerState::new());
79 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
80 let config = DiagnosticsConfig::default();
81
82 let (client, full_config) = create_test_client_and_config();
83 let result = handle_diagnostics(state, &uri, &config, client, full_config).await;
84 assert!(result.is_empty());
85 }
86
87 #[cfg(feature = "cargo")]
89 mod cargo_tests {
90 use super::*;
91 use crate::document::{DocumentState, Ecosystem};
92
93 #[tokio::test]
94 async fn test_handle_diagnostics() {
95 let state = Arc::new(ServerState::new());
96 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
97 let config = DiagnosticsConfig::default();
98
99 let ecosystem = state.ecosystem_registry.get("cargo").unwrap();
100 let content = r#"[dependencies]
101serde = "1.0.0"
102"#
103 .to_string();
104
105 let parse_result = ecosystem
106 .parse_manifest(&content, &uri)
107 .await
108 .expect("Failed to parse manifest");
109
110 let doc_state = DocumentState::new_from_parse_result("cargo", content, parse_result);
111 state.update_document(uri.clone(), doc_state);
112
113 let (client, full_config) = create_test_client_and_config();
114 let _result = handle_diagnostics(state, &uri, &config, client, full_config).await;
115 }
117
118 #[tokio::test]
119 async fn test_handle_diagnostics_no_parse_result() {
120 let state = Arc::new(ServerState::new());
121 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
122 let config = DiagnosticsConfig::default();
123
124 let doc_state = DocumentState::new(Ecosystem::Cargo, String::new(), vec![]);
125 state.update_document(uri.clone(), doc_state);
126
127 let (client, full_config) = create_test_client_and_config();
128 let result = handle_diagnostics(state, &uri, &config, client, full_config).await;
129 assert!(result.is_empty());
130 }
131 }
132
133 #[cfg(feature = "npm")]
135 mod npm_tests {
136 use super::*;
137 use crate::document::DocumentState;
138
139 #[tokio::test]
140 async fn test_handle_diagnostics() {
141 let state = Arc::new(ServerState::new());
142 let uri = Uri::from_file_path("/test/package.json").unwrap();
143 let config = DiagnosticsConfig::default();
144
145 let ecosystem = state.ecosystem_registry.get("npm").unwrap();
146 let content = r#"{"dependencies": {"express": "4.0.0"}}"#.to_string();
147
148 let parse_result = ecosystem
149 .parse_manifest(&content, &uri)
150 .await
151 .expect("Failed to parse manifest");
152
153 let doc_state = DocumentState::new_from_parse_result("npm", content, parse_result);
154 state.update_document(uri.clone(), doc_state);
155
156 let (client, full_config) = create_test_client_and_config();
157 let _result = handle_diagnostics(state, &uri, &config, client, full_config).await;
158 }
160 }
161
162 #[cfg(feature = "pypi")]
164 mod pypi_tests {
165 use super::*;
166 use crate::document::DocumentState;
167
168 #[tokio::test]
169 async fn test_handle_diagnostics() {
170 let state = Arc::new(ServerState::new());
171 let uri = Uri::from_file_path("/test/pyproject.toml").unwrap();
172 let config = DiagnosticsConfig::default();
173
174 let ecosystem = state.ecosystem_registry.get("pypi").unwrap();
175 let content = r#"[project]
176dependencies = ["requests>=2.0.0"]
177"#
178 .to_string();
179
180 let parse_result = ecosystem
181 .parse_manifest(&content, &uri)
182 .await
183 .expect("Failed to parse manifest");
184
185 let doc_state = DocumentState::new_from_parse_result("pypi", content, parse_result);
186 state.update_document(uri.clone(), doc_state);
187
188 let (client, full_config) = create_test_client_and_config();
189 let _result = handle_diagnostics(state, &uri, &config, client, full_config).await;
190 }
192 }
193}