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