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