use crate::{Run, RunSync};
use lutra_bin::{rr, string, vec};
use std::sync::Arc;
use tokio::runtime::Runtime;
pub struct SyncRunner<R> {
runner: Arc<R>,
rt: Runtime,
}
impl<R> SyncRunner<R> {
pub fn new(runner: R) -> Self
where
R: Send + Sync + 'static,
{
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.expect("failed to create Tokio runtime");
Self::with_runtime(runner, runtime)
}
pub fn with_runtime(runner: R, rt: Runtime) -> Self {
Self {
runner: Arc::new(runner),
rt,
}
}
pub fn inner(&self) -> &R {
&self.runner
}
pub fn runtime(&self) -> &Runtime {
&self.rt
}
}
impl<R> RunSync for SyncRunner<R>
where
R: Run + Send + Sync + 'static,
R::Prepared: Clone + Send + Sync,
R::Error: Send,
{
type Error = R::Error;
type Prepared = R::Prepared;
fn prepare_sync(&mut self, program: rr::Program) -> Result<Self::Prepared, Self::Error> {
let runner = Arc::clone(&self.runner);
self.rt
.block_on(async move { runner.prepare(program).await })
}
fn execute_sync(
&mut self,
program: &Self::Prepared,
input: &[u8],
) -> Result<vec::Vec<u8>, Self::Error> {
let runner = Arc::clone(&self.runner);
self.rt
.block_on(async move { runner.execute(program, input).await })
}
fn get_interface_sync(&mut self) -> Result<string::String, Self::Error> {
let runner = Arc::clone(&self.runner);
self.rt
.block_on(async move { runner.get_interface().await })
}
fn shutdown_sync(&mut self) -> Result<(), Self::Error> {
let runner = Arc::clone(&self.runner);
self.rt.block_on(async move { runner.shutdown().await })
}
}
impl<R> Clone for SyncRunner<R> {
fn clone(&self) -> Self {
Self {
runner: Arc::clone(&self.runner),
rt: Runtime::new().expect("failed to create Tokio runtime"),
}
}
}
impl<R> std::fmt::Debug for SyncRunner<R>
where
R: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SyncRunner")
.field("runner", &self.runner)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockRunner;
impl Run for MockRunner {
type Error = String;
type Prepared = String;
async fn prepare(&self, _program: rr::Program) -> Result<Self::Prepared, Self::Error> {
Ok("prepared".to_string())
}
async fn execute(
&self,
_program: &Self::Prepared,
input: &[u8],
) -> Result<vec::Vec<u8>, Self::Error> {
Ok(vec::Vec::from(input))
}
async fn get_interface(&self) -> Result<string::String, Self::Error> {
Ok(string::String::from("mock interface"))
}
async fn shutdown(&self) -> Result<(), Self::Error> {
Ok(())
}
}
#[test]
fn test_sync_runner_basic() {
let mock = MockRunner;
let mut sync_runner = SyncRunner::new(mock);
use lutra_bin::br;
let program = rr::Program::BytecodeLt(br::Program {
externals: vec::Vec::new(),
main: br::Expr {
kind: br::ExprKind::Literal(vec::Vec::new()),
},
defs: vec::Vec::new(),
});
let prepared = sync_runner.prepare_sync(program).unwrap();
assert_eq!(prepared, "prepared");
let input_data = b"hello world";
let output = sync_runner.execute_sync(&prepared, input_data).unwrap();
assert_eq!(&output[..], input_data);
let interface = sync_runner.get_interface_sync().unwrap();
assert_eq!(&interface[..], "mock interface");
sync_runner.shutdown_sync().unwrap();
}
}