prospero_core/caliband/
mod.rs1pub mod client;
5pub mod sources;
6pub mod stream;
7pub mod transport;
8pub mod wire;
9
10use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
11
12use crate::error::{CoreError, Result};
13
14pub(crate) async fn write_frame<W, T>(w: &mut W, value: &T) -> Result<()>
16where
17 W: AsyncWriteExt + Unpin,
18 T: serde::Serialize,
19{
20 let mut line = serde_json::to_vec(value)?;
21 line.push(b'\n');
22 w.write_all(&line).await?;
23 w.flush().await?;
24 Ok(())
25}
26
27pub(crate) async fn read_frame<R, T>(r: &mut R) -> Result<T>
30where
31 R: AsyncBufReadExt + Unpin,
32 T: serde::de::DeserializeOwned,
33{
34 let mut line = String::new();
35 let n = r.read_line(&mut line).await?;
36 if n == 0 {
37 return Err(CoreError::Protocol(
38 "connection closed before a reply frame was read".into(),
39 ));
40 }
41 let value = serde_json::from_str(line.trim_end())?;
42 Ok(value)
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use crate::caliband::wire::CtlRequest;
49 use tokio::io::BufReader;
50
51 #[tokio::test]
52 async fn frame_round_trips_through_a_pipe() {
53 let mut buf: Vec<u8> = Vec::new();
55 write_frame(&mut buf, &CtlRequest::List).await.unwrap();
56 assert_eq!(buf, b"{\"kind\":\"list\"}\n");
57
58 let mut reader = BufReader::new(&buf[..]);
59 let req: CtlRequest = read_frame(&mut reader).await.unwrap();
60 assert_eq!(req, CtlRequest::List);
61 }
62
63 #[tokio::test]
64 async fn read_frame_errors_on_empty_stream() {
65 let empty: &[u8] = b"";
66 let mut reader = BufReader::new(empty);
67 let r: Result<CtlRequest> = read_frame(&mut reader).await;
68 assert!(matches!(r, Err(CoreError::Protocol(_))));
69 }
70}