97 lines
2.8 KiB
Rust
97 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,
|
|
Shutdown,
|
|
}
|
|
|
|
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.
|
|
});
|
|
}
|
|
|
|
/// Register SIGTERM and SIGINT before the harness starts.
|
|
pub 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 receiver.
|
|
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::Shutdown);
|
|
})
|
|
}
|