プロトコルStreamのユーティリティ共通化
This commit is contained in:
@@ -7,3 +7,4 @@ license.workspace = true
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.51.1", features = ["io-util"] }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod stream;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -12,12 +14,6 @@ pub enum Method {
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl Method {
|
||||
pub fn from_json_line(line: &str) -> Result<Self, serde_json::Error> {
|
||||
serde_json::from_str(line)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Event (Pod → Client via Unix Socket broadcast)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -69,12 +65,6 @@ pub enum Event {
|
||||
},
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn to_json_line(&self) -> Result<String, serde_json::Error> {
|
||||
serde_json::to_string(self)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Supporting types
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -112,7 +102,7 @@ mod tests {
|
||||
#[test]
|
||||
fn method_run_json_roundtrip() {
|
||||
let json = r#"{"method":"run","params":{"input":"Hello"}}"#;
|
||||
let method = Method::from_json_line(json).unwrap();
|
||||
let method: Method = serde_json::from_str(json).unwrap();
|
||||
assert!(matches!(method, Method::Run { ref input } if input == "Hello"));
|
||||
|
||||
let serialized = serde_json::to_string(&method).unwrap();
|
||||
@@ -122,7 +112,7 @@ mod tests {
|
||||
#[test]
|
||||
fn method_without_params() {
|
||||
let json = r#"{"method":"resume"}"#;
|
||||
let method = Method::from_json_line(json).unwrap();
|
||||
let method: Method = serde_json::from_str(json).unwrap();
|
||||
assert!(matches!(method, Method::Resume));
|
||||
}
|
||||
|
||||
@@ -131,7 +121,7 @@ mod tests {
|
||||
let event = Event::TextDelta {
|
||||
text: "Hello".into(),
|
||||
};
|
||||
let json = event.to_json_line().unwrap();
|
||||
let json = serde_json::to_string(&event).unwrap();
|
||||
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(parsed["event"], "text_delta");
|
||||
assert_eq!(parsed["data"]["text"], "Hello");
|
||||
@@ -142,7 +132,7 @@ mod tests {
|
||||
let event = Event::RunEnd {
|
||||
result: RunResult::LimitReached,
|
||||
};
|
||||
let json = event.to_json_line().unwrap();
|
||||
let json = serde_json::to_string(&event).unwrap();
|
||||
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(parsed["event"], "run_end");
|
||||
assert_eq!(parsed["data"]["result"], "limit_reached");
|
||||
@@ -154,7 +144,7 @@ mod tests {
|
||||
code: ErrorCode::AlreadyRunning,
|
||||
message: "Pod is already executing a turn".into(),
|
||||
};
|
||||
let json = event.to_json_line().unwrap();
|
||||
let json = serde_json::to_string(&event).unwrap();
|
||||
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(parsed["event"], "error");
|
||||
assert_eq!(parsed["data"]["code"], "already_running");
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
use std::io;
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt, BufReader};
|
||||
|
||||
/// JSONL line reader over an async byte stream.
|
||||
///
|
||||
/// Wraps the inner reader and deserialises each non‑empty line as `T`.
|
||||
pub struct JsonLineReader<R> {
|
||||
inner: R,
|
||||
}
|
||||
|
||||
impl<R: tokio::io::AsyncRead + Unpin> JsonLineReader<BufReader<R>> {
|
||||
/// Wrap a raw reader (internally creates a [`BufReader`]).
|
||||
pub fn new(reader: R) -> Self {
|
||||
Self {
|
||||
inner: BufReader::new(reader),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncBufRead + Unpin> JsonLineReader<R> {
|
||||
/// Read and deserialise the next non‑empty JSONL line.
|
||||
///
|
||||
/// Returns `Ok(None)` on EOF.
|
||||
pub async fn next<T: DeserializeOwned>(&mut self) -> Result<Option<T>, io::Error> {
|
||||
let mut line = String::new();
|
||||
loop {
|
||||
line.clear();
|
||||
let n = self.inner.read_line(&mut line).await?;
|
||||
if n == 0 {
|
||||
return Ok(None); // EOF
|
||||
}
|
||||
let trimmed = line.trim();
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let value = serde_json::from_str(trimmed)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||
return Ok(Some(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// JSONL line writer over an async byte stream.
|
||||
pub struct JsonLineWriter<W> {
|
||||
inner: W,
|
||||
}
|
||||
|
||||
impl<W: AsyncWrite + Unpin> JsonLineWriter<W> {
|
||||
pub fn new(writer: W) -> Self {
|
||||
Self { inner: writer }
|
||||
}
|
||||
|
||||
/// Serialise `value` as a single JSONL line and flush.
|
||||
pub async fn write<T: Serialize>(&mut self, value: &T) -> Result<(), io::Error> {
|
||||
let json = serde_json::to_string(value)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||
self.inner.write_all(json.as_bytes()).await?;
|
||||
self.inner.write_all(b"\n").await?;
|
||||
self.inner.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user