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 if doc.loading_state == deps_core::LoadingState::Loading {
54 return vec![];
55 }
56
57 let parse_result = match doc.parse_result() {
58 Some(p) => p,
59 None => return vec![],
60 };
61
62 ecosystem
64 .generate_diagnostics(
65 parse_result,
66 &doc.cached_versions,
67 &doc.resolved_versions,
68 uri,
69 )
70 .await
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::config::DiagnosticsConfig;
77 use crate::document::ServerState;
78 use crate::test_utils::test_helpers::create_test_client_and_config;
79
80 #[tokio::test]
83 async fn test_handle_diagnostics_missing_document() {
84 let state = Arc::new(ServerState::new());
85 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
86 let config = DiagnosticsConfig::default();
87
88 let (client, full_config) = create_test_client_and_config();
89 let result = handle_diagnostics(state, &uri, &config, client, full_config).await;
90 assert!(result.is_empty());
91 }
92
93 #[cfg(feature = "cargo")]
95 mod cargo_tests {
96 use super::*;
97 use crate::document::{DocumentState, Ecosystem};
98
99 #[tokio::test]
100 async fn test_handle_diagnostics() {
101 let state = Arc::new(ServerState::new());
102 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
103 let config = DiagnosticsConfig::default();
104
105 let ecosystem = state.ecosystem_registry.get("cargo").unwrap();
106 let content = r#"[dependencies]
107serde = "1.0.0"
108"#
109 .to_string();
110
111 let parse_result = ecosystem
112 .parse_manifest(&content, &uri)
113 .await
114 .expect("Failed to parse manifest");
115
116 let doc_state = DocumentState::new_from_parse_result("cargo", content, parse_result);
117 state.update_document(uri.clone(), doc_state);
118
119 let (client, full_config) = create_test_client_and_config();
120 let _result = handle_diagnostics(state, &uri, &config, client, full_config).await;
121 }
123
124 #[tokio::test]
125 async fn test_handle_diagnostics_no_parse_result() {
126 let state = Arc::new(ServerState::new());
127 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
128 let config = DiagnosticsConfig::default();
129
130 let doc_state = DocumentState::new(Ecosystem::Cargo, String::new(), vec![]);
131 state.update_document(uri.clone(), doc_state);
132
133 let (client, full_config) = create_test_client_and_config();
134 let result = handle_diagnostics(state, &uri, &config, client, full_config).await;
135 assert!(result.is_empty());
136 }
137 }
138
139 #[cfg(feature = "npm")]
141 mod npm_tests {
142 use super::*;
143 use crate::document::DocumentState;
144
145 #[tokio::test]
146 async fn test_handle_diagnostics() {
147 let state = Arc::new(ServerState::new());
148 let uri = Uri::from_file_path("/test/package.json").unwrap();
149 let config = DiagnosticsConfig::default();
150
151 let ecosystem = state.ecosystem_registry.get("npm").unwrap();
152 let content = r#"{"dependencies": {"express": "4.0.0"}}"#.to_string();
153
154 let parse_result = ecosystem
155 .parse_manifest(&content, &uri)
156 .await
157 .expect("Failed to parse manifest");
158
159 let doc_state = DocumentState::new_from_parse_result("npm", content, parse_result);
160 state.update_document(uri.clone(), doc_state);
161
162 let (client, full_config) = create_test_client_and_config();
163 let _result = handle_diagnostics(state, &uri, &config, client, full_config).await;
164 }
166 }
167
168 #[cfg(feature = "pypi")]
170 mod pypi_tests {
171 use super::*;
172 use crate::document::DocumentState;
173
174 #[tokio::test]
175 async fn test_handle_diagnostics() {
176 let state = Arc::new(ServerState::new());
177 let uri = Uri::from_file_path("/test/pyproject.toml").unwrap();
178 let config = DiagnosticsConfig::default();
179
180 let ecosystem = state.ecosystem_registry.get("pypi").unwrap();
181 let content = r#"[project]
182dependencies = ["requests>=2.0.0"]
183"#
184 .to_string();
185
186 let parse_result = ecosystem
187 .parse_manifest(&content, &uri)
188 .await
189 .expect("Failed to parse manifest");
190
191 let doc_state = DocumentState::new_from_parse_result("pypi", content, parse_result);
192 state.update_document(uri.clone(), doc_state);
193
194 let (client, full_config) = create_test_client_and_config();
195 let _result = handle_diagnostics(state, &uri, &config, client, full_config).await;
196 }
198 }
199}