use std::future::Future;
use crate::runtime::BlockId;
use crate::runtime::BlockPortCtx;
use crate::runtime::Error;
use crate::runtime::Pmt;
use crate::runtime::PortId;
use crate::runtime::PortIndex;
use crate::runtime::PortName;
use crate::runtime::Result;
use crate::runtime::buffer::DynBufferReader;
use crate::runtime::buffer::DynBufferWriter;
use crate::runtime::buffer::PortInboxes;
use crate::runtime::dev::BlockMeta;
use crate::runtime::dev::MessageOutputs;
use crate::runtime::dev::WorkIo;
#[doc(hidden)]
pub trait KernelInterface {
fn is_blocking() -> bool;
fn type_name() -> &'static str;
fn stream_input_at(&mut self, index: PortIndex)
-> Option<(PortName, &mut dyn DynBufferReader)>;
fn stream_output_at(
&mut self,
index: PortIndex,
) -> Option<(PortName, &mut dyn DynBufferWriter)>;
fn stream_ports_notify_finished(&mut self) -> impl Future<Output = ()>;
fn message_inputs() -> &'static [&'static str];
fn message_outputs() -> &'static [&'static str];
fn message_input_id(name: impl Into<PortName>) -> Option<PortIndex> {
let name = name.into();
Self::message_inputs()
.iter()
.position(|candidate| *candidate == name.as_str())
.map(PortIndex::new)
}
fn call_handler(
&mut self,
_io: &mut WorkIo,
_mo: &mut MessageOutputs,
_meta: &BlockMeta,
id: PortIndex,
_p: Pmt,
) -> impl Future<Output = Result<Pmt, Error>>;
}
pub(crate) fn stream_input_names<K: KernelInterface>(kernel: &mut K) -> Vec<String> {
let mut names = Vec::new();
let mut index = 0;
while let Some((name, _)) = kernel.stream_input_at(PortIndex::new(index)) {
names.push(name.into_string());
index += 1;
}
names
}
pub(crate) fn stream_output_names<K: KernelInterface>(kernel: &mut K) -> Vec<String> {
let mut names = Vec::new();
let mut index = 0;
while let Some((name, _)) = kernel.stream_output_at(PortIndex::new(index)) {
names.push(name.into_string());
index += 1;
}
names
}
pub(crate) fn stream_ports_init<K: KernelInterface>(
kernel: &mut K,
block_id: BlockId,
inboxes: PortInboxes,
) {
let mut index = 0;
while let Some((_, port)) = kernel.stream_input_at(PortIndex::new(index)) {
port.init_from(block_id, PortIndex::new(index), &inboxes);
index += 1;
}
let mut index = 0;
while let Some((_, port)) = kernel.stream_output_at(PortIndex::new(index)) {
port.init_from(block_id, PortIndex::new(index), &inboxes);
index += 1;
}
}
pub(crate) fn stream_ports_validate<K: KernelInterface>(kernel: &mut K) -> Result<(), Error> {
let mut index = 0;
while let Some((_, port)) = kernel.stream_input_at(PortIndex::new(index)) {
port.validate()?;
index += 1;
}
let mut index = 0;
while let Some((_, port)) = kernel.stream_output_at(PortIndex::new(index)) {
port.validate()?;
index += 1;
}
Ok(())
}
pub(crate) fn stream_input_finish<K: KernelInterface>(
kernel: &mut K,
block_id: BlockId,
index: PortIndex,
) -> Result<(), Error> {
let (_, port) = kernel
.stream_input_at(index)
.ok_or_else(|| Error::InvalidStreamPort(BlockPortCtx::Id(block_id), PortId::from(index)))?;
port.finish();
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
struct MissingPorts;
impl KernelInterface for MissingPorts {
fn is_blocking() -> bool {
false
}
fn type_name() -> &'static str {
"MissingPorts"
}
fn stream_input_at(
&mut self,
_index: PortIndex,
) -> Option<(PortName, &mut dyn DynBufferReader)> {
None
}
fn stream_output_at(
&mut self,
_index: PortIndex,
) -> Option<(PortName, &mut dyn DynBufferWriter)> {
None
}
async fn stream_ports_notify_finished(&mut self) {}
fn message_inputs() -> &'static [&'static str] {
&[]
}
fn message_outputs() -> &'static [&'static str] {
&[]
}
async fn call_handler(
&mut self,
_io: &mut WorkIo,
_mo: &mut MessageOutputs,
_meta: &BlockMeta,
id: PortIndex,
_p: Pmt,
) -> Result<Pmt, Error> {
Err(Error::InvalidMessagePort(
BlockPortCtx::None,
PortId::from(id),
))
}
}
#[test]
fn stream_input_finish_invalid_port_reports_block_id() {
let mut kernel = MissingPorts;
let result = stream_input_finish(&mut kernel, BlockId(7), PortIndex::new(3));
assert!(matches!(
result,
Err(Error::InvalidStreamPort(ctx, port))
if ctx == BlockPortCtx::Id(BlockId(7)) && port == PortId::index(3)
));
}
}