1mod completion;
10mod configuration;
11pub mod index_worker;
12mod linting;
13mod navigation;
14pub mod server;
15pub mod types;
16
17pub use server::RumdlLanguageServer;
18pub use types::{RumdlLspConfig, warning_to_code_actions, warning_to_diagnostic};
19
20use anyhow::Result;
21use tokio::net::TcpListener;
22use tower_lsp::{LspService, Server};
23
24pub async fn start_server(config_path: Option<&str>) -> Result<()> {
27 let stdin = tokio::io::stdin();
28 let stdout = tokio::io::stdout();
29
30 let (service, socket) = LspService::new(|client| RumdlLanguageServer::new(client, config_path));
31
32 log::info!("Starting rumdl Language Server Protocol server");
33
34 Server::new(stdin, stdout, socket).serve(service).await;
35
36 Ok(())
37}
38
39pub async fn start_tcp_server(port: u16, config_path: Option<&str>) -> Result<()> {
41 let listener = TcpListener::bind(format!("127.0.0.1:{port}")).await?;
42 log::info!("rumdl LSP server listening on 127.0.0.1:{port}");
43
44 let config_path_owned = config_path.map(|s| s.to_string());
46
47 loop {
48 let (stream, _) = listener.accept().await?;
49 let config_path_clone = config_path_owned.clone();
50 let (service, socket) =
51 LspService::new(move |client| RumdlLanguageServer::new(client, config_path_clone.as_deref()));
52
53 tokio::spawn(async move {
54 let (read, write) = tokio::io::split(stream);
55 Server::new(read, write, socket).serve(service).await;
56 });
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn test_module_exports() {
66 fn _check_exports() {
69 let _server_type: RumdlLanguageServer;
71 let _config_type: RumdlLspConfig;
72 let _func1: fn(&crate::rule::LintWarning) -> tower_lsp::lsp_types::Diagnostic = warning_to_diagnostic;
73 let _func2: fn(
74 &crate::rule::LintWarning,
75 &tower_lsp::lsp_types::Url,
76 &str,
77 ) -> Vec<tower_lsp::lsp_types::CodeAction> = warning_to_code_actions;
78 }
79 }
80
81 #[tokio::test]
82 async fn test_tcp_server_bind() {
83 use std::net::TcpListener as StdTcpListener;
84
85 let listener = StdTcpListener::bind("127.0.0.1:0").unwrap();
87 let port = listener.local_addr().unwrap().port();
88 drop(listener);
89
90 let server_handle = tokio::spawn(async move {
92 match tokio::time::timeout(std::time::Duration::from_millis(100), start_tcp_server(port, None)).await {
94 Ok(Ok(())) => {} Ok(Err(_)) => {} Err(_) => {} }
98 });
99
100 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
102
103 match tokio::time::timeout(
105 std::time::Duration::from_millis(50),
106 tokio::net::TcpStream::connect(format!("127.0.0.1:{port}")),
107 )
108 .await
109 {
110 Ok(Ok(_)) => {
111 }
113 _ => {
114 }
116 }
117
118 server_handle.abort();
120 }
121
122 #[tokio::test]
123 async fn test_tcp_server_invalid_port() {
124 let result = tokio::time::timeout(std::time::Duration::from_millis(100), start_tcp_server(80, None)).await;
127
128 match result {
129 Ok(Err(_)) => {
130 }
132 Ok(Ok(())) => {
133 panic!("Should not be able to bind to port 80 without privileges");
134 }
135 Err(_) => {
136 }
139 }
140 }
141
142 #[tokio::test]
143 async fn test_service_creation() {
144 let (service, _socket) = LspService::new(|client| RumdlLanguageServer::new(client, None));
146
147 drop(service);
150 }
151
152 #[tokio::test]
153 async fn test_multiple_tcp_connections() {
154 use std::net::TcpListener as StdTcpListener;
155
156 let listener = StdTcpListener::bind("127.0.0.1:0").unwrap();
158 let port = listener.local_addr().unwrap().port();
159 drop(listener);
160
161 let server_handle = tokio::spawn(async move {
163 let _ = tokio::time::timeout(std::time::Duration::from_millis(500), start_tcp_server(port, None)).await;
164 });
165
166 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
168
169 let mut handles = vec![];
171 for _ in 0..3 {
172 let handle = tokio::spawn(async move {
173 match tokio::time::timeout(
174 std::time::Duration::from_millis(100),
175 tokio::net::TcpStream::connect(format!("127.0.0.1:{port}")),
176 )
177 .await
178 {
179 Ok(Ok(_stream)) => {
180 true
182 }
183 _ => false,
184 }
185 });
186 handles.push(handle);
187 }
188
189 for handle in handles {
191 let _ = handle.await;
192 }
193
194 server_handle.abort();
196 }
197
198 #[test]
199 fn test_logging_initialization() {
200 let _info_level = log::Level::Info;
206 }
207}