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