mirage 0.0.1

LSP proxying between build servers and local machines
use std::io;
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
use tracing::{debug, trace};

/// Parse an LSP message from a reader
/// Format: Content-Length: <bytes>\r\n\r\n<json>
pub async fn read_lsp_message<R: AsyncReadExt + Unpin>(
    reader: &mut BufReader<R>,
) -> io::Result<Option<String>> {
    let mut content_length: Option<usize> = None;

    // Read headers until we find the blank line
    loop {
        let mut line = String::new();
        let bytes_read = reader.read_line(&mut line).await?;

        if bytes_read == 0 {
            return Ok(None); // EOF
        }

        trace!("Read header line: {:?}", line);

        // Blank line signals end of headers
        if line == "\r\n" {
            break;
        }

        // Parse Content-Length header
        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")
    })?;

    // Read the exact number of bytes specified by Content-Length
    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))
}

/// Write an LSP message to a writer
/// Format: Content-Length: <bytes>\r\n\r\n<json>
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(())
}