use dora_core::{
config::NodeId,
descriptor::{Descriptor, OperatorDefinition, OperatorSource},
};
use dora_node_api::Event;
use dora_runtime_api::{OperatorRunner, RunnerGuard, RuntimeHandle};
use eyre::{Context, Result};
use tokio::sync::oneshot;
mod runner;
pub fn main() -> eyre::Result<()> {
dora_runtime_api::main(SharedLibRunner)
}
pub struct SharedLibRunner;
impl OperatorRunner for SharedLibRunner {
fn run_operator(
&self,
node_id: &NodeId,
operator: OperatorDefinition,
incoming_events: flume::Receiver<Event>,
handle: RuntimeHandle,
init_done: oneshot::Sender<Result<()>>,
_dataflow_descriptor: &Descriptor,
) -> eyre::Result<RunnerGuard> {
match &operator.config.source {
OperatorSource::SharedLibrary(source) => runner::run(
node_id,
&operator.id,
source,
handle,
incoming_events,
init_done,
)
.wrap_err_with(|| {
format!(
"failed to spawn shared library operator for {}",
operator.id
)
})
.map(|library| Some(Box::new(library) as Box<dyn std::any::Any>)),
OperatorSource::Python(_) => eyre::bail!(
"operator `{}` uses a Python source, but this is the shared-library \
runtime; Python operators are spawned by the Python runtime \
(`dora-runtime-python`)",
operator.id
),
OperatorSource::Wasm(_) => eyre::bail!(
"operator `{}` uses a WASM source, which is not supported yet",
operator.id
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use dora_runtime_api::SharedAllocator;
fn run_unsupported(yaml: &str) -> (eyre::Report, oneshot::Receiver<Result<()>>) {
let operator: OperatorDefinition =
serde_yaml::from_str(yaml).expect("operator definition parses");
let dataflow: Descriptor =
serde_yaml::from_str("nodes:\n - id: a\n").expect("descriptor parses");
let (_events_in_tx, incoming_events) = flume::unbounded::<Event>();
let (events_tx, _events_rx) = tokio::sync::mpsc::channel(1);
let (init_done_tx, init_done_rx) = oneshot::channel();
let err = SharedLibRunner
.run_operator(
&NodeId::from("node".to_string()),
operator,
incoming_events,
RuntimeHandle::new(events_tx, SharedAllocator::default()),
init_done_tx,
&dataflow,
)
.expect_err("unsupported operator source must return an error");
(err, init_done_rx)
}
#[test]
fn wasm_source_returns_descriptive_error() {
let (err, mut init_done_rx) = run_unsupported("id: op\nwasm: model.wasm\n");
assert!(
err.to_string().contains("WASM"),
"expected a descriptive WASM error, got: {err}"
);
assert!(
init_done_rx.try_recv().is_err(),
"init_done must not receive a value for an unsupported source"
);
}
#[test]
fn python_source_returns_descriptive_error() {
let (err, mut init_done_rx) = run_unsupported("id: op\npython: op.py\n");
let msg = err.to_string();
assert!(
msg.contains("Python") && msg.contains("shared-library runtime"),
"expected an error naming the wrong runtime, got: {err}"
);
assert!(
init_done_rx.try_recv().is_err(),
"init_done must not receive a value for a wrongly routed source"
);
}
}