Skip to main content

vtcode_desktop_protocol/
frames.rs

1//! Length-delimited JSON-RPC frame transport.
2use std::io;
3
4use serde::Serialize;
5use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt};
6
7use super::{MAX_FRAME_BYTES, ProtocolError};
8
9/// Read one bounded JSON-RPC line. A final unterminated line is accepted at EOF.
10pub async fn read_frame<R>(reader: &mut R) -> io::Result<Option<Vec<u8>>>
11where
12    R: AsyncBufRead + Unpin,
13{
14    let mut frame = Vec::with_capacity(1024);
15    loop {
16        let available = reader.fill_buf().await?;
17        if available.is_empty() {
18            return if frame.is_empty() { Ok(None) } else { Ok(Some(frame)) };
19        }
20
21        let newline = available.iter().position(|byte| *byte == b'\n');
22        let take = newline.map_or(available.len(), |index| index + 1);
23        let content_end = if newline.is_some() && available.get(take.saturating_sub(1)) == Some(&b'\n') {
24            take.saturating_sub(1)
25        } else {
26            take
27        };
28        let content_end = if content_end > 0 && available.get(content_end.saturating_sub(1)) == Some(&b'\r') {
29            content_end.saturating_sub(1)
30        } else {
31            content_end
32        };
33
34        if frame.len().saturating_add(content_end) > MAX_FRAME_BYTES {
35            reader.consume(take);
36            return Err(io::Error::new(
37                io::ErrorKind::InvalidData,
38                ProtocolError::FrameTooLarge { limit: MAX_FRAME_BYTES },
39            ));
40        }
41        let content = available
42            .get(..content_end)
43            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid buffered frame boundary"))?;
44        frame.extend_from_slice(content);
45        reader.consume(take);
46
47        if newline.is_some() {
48            return Ok(Some(frame));
49        }
50    }
51}
52
53/// Write one compact JSON-RPC line and flush it to the client.
54pub async fn write_frame<W, T>(writer: &mut W, message: &T) -> io::Result<()>
55where
56    W: AsyncWrite + Unpin,
57    T: Serialize,
58{
59    let encoded = serde_json::to_vec(message).map_err(io::Error::other)?;
60    if encoded.len() > MAX_FRAME_BYTES {
61        return Err(io::Error::new(
62            io::ErrorKind::InvalidData,
63            ProtocolError::FrameTooLarge { limit: MAX_FRAME_BYTES },
64        ));
65    }
66    writer.write_all(&encoded).await?;
67    writer.write_all(b"\n").await?;
68    writer.flush().await
69}