use std::io;
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
use tracing::{debug, trace};
pub async fn read_lsp_message<R: AsyncReadExt + Unpin>(
reader: &mut BufReader<R>,
) -> io::Result<Option<String>> {
let mut content_length: Option<usize> = None;
loop {
let mut line = String::new();
let bytes_read = reader.read_line(&mut line).await?;
if bytes_read == 0 {
return Ok(None); }
trace!("Read header line: {:?}", line);
if line == "\r\n" {
break;
}
if let Some(value) = line.strip_prefix("Content-Length: ") {
content_length = value.trim().parse().ok();
}
}
let content_length = content_length.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "Missing Content-Length header")
})?;
let mut buffer = vec![0u8; content_length];
reader.read_exact(&mut buffer).await?;
let message =
String::from_utf8(buffer).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
debug!("Received LSP message ({} bytes)", content_length);
trace!("Message content: {}", message);
Ok(Some(message))
}
pub async fn write_lsp_message<W: AsyncWriteExt + Unpin>(
writer: &mut W,
message: &str,
) -> io::Result<()> {
let content_length = message.len();
let header = format!("Content-Length: {}\r\n\r\n", content_length);
trace!("Sending LSP message ({} bytes)", content_length);
trace!("Message content: {}", message);
writer.write_all(header.as_bytes()).await?;
writer.write_all(message.as_bytes()).await?;
writer.flush().await?;
Ok(())
}