Files
sausage/host/src/harness/input.rs
T

95 lines
2.8 KiB
Rust

//! Channel input for a headless harness. The console and stop signals are clients.
use deno_core::{OpState, op2};
use deno_error::JsErrorBox;
use serde::Serialize;
use serde_json::Value;
use std::{cell::RefCell, rc::Rc};
use tokio::sync::mpsc;
#[derive(Serialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum Input {
Submit {
conversation: u64,
content: Value,
#[serde(rename = "requestId")]
request_id: String,
},
Api {
id: String,
method: String,
path: String,
body: Value,
#[serde(skip)]
reply: mpsc::UnboundedSender<Value>,
},
Stop,
}
pub type Sender = mpsc::UnboundedSender<Input>;
pub type Receiver = mpsc::UnboundedReceiver<Input>;
pub fn channel() -> (Sender, Receiver) {
mpsc::unbounded_channel()
}
pub(super) struct Inbox(pub tokio::sync::Mutex<Receiver>);
#[op2]
#[serde]
pub async fn op_input(state: Rc<RefCell<OpState>>) -> Result<serde_json::Value, JsErrorBox> {
let inbox = state.borrow().borrow::<Rc<Inbox>>().clone();
let input = inbox.0.lock().await.recv().await;
if let Some(Input::Api { id, reply, .. }) = &input {
state
.borrow_mut()
.borrow_mut::<super::http::Replies>()
.insert(
id.clone(),
super::http::Reply {
sender: reply.clone(),
finished: Default::default(),
},
);
}
serde_json::to_value(input).map_err(|error| JsErrorBox::generic(error.to_string()))
}
pub(super) fn console(sender: Sender) {
std::thread::spawn(move || {
use std::io::BufRead;
for line in std::io::stdin().lock().lines() {
let Ok(line) = line else { break };
if line.trim().is_empty() {
continue;
}
if sender
.send(Input::Submit {
conversation: 1,
content: Value::String(line),
request_id: uuid::Uuid::new_v4().to_string(),
})
.is_err()
{
break;
}
}
// EOF closes only this client. The signal client keeps the harness alive.
});
}
pub(super) fn stop_signal(
sender: Sender,
) -> std::io::Result<impl std::future::Future<Output = ()> + Send> {
// Register before V8 starts, so a stop cannot arrive before the listener.
let mut terminate = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())?;
let mut interrupt = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt())?;
Ok(async move {
tokio::select! {
_ = interrupt.recv() => {}
_ = terminate.recv() => {}
}
let _ = sender.send(Input::Stop);
})
}