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