use crate::effect::{Effect, EffectSink};
use crate::error::CoreError;
use crate::module_host::{CorrelationMap, ModuleHost, TimerMap};
use crate::tick::Tick;
pub struct ExecutionShell {
host: ModuleHost,
corr_map: CorrelationMap,
timer_map: TimerMap,
scratch: EffectSink,
}
impl ExecutionShell {
pub fn new() -> Self {
Self {
host: ModuleHost::new(),
corr_map: CorrelationMap::default(),
timer_map: TimerMap::default(),
scratch: EffectSink::new(),
}
}
pub fn register(&mut self, m: impl crate::module_host::Module + 'static) {
self.host.register(m);
}
pub fn start(&mut self) -> Result<&[Effect], CoreError> {
self.scratch.clear();
let module_count = self.host.module_count();
for idx in 0..module_count {
let start = self.scratch.as_slice().len();
self.host.start_one(idx, &mut self.scratch)?;
self.register_effects_to_maps(idx, start);
}
Ok(self.scratch.as_slice())
}
pub fn stop(&mut self) -> Result<&[Effect], CoreError> {
self.scratch.clear();
self.host.stop_all(&mut self.scratch)?;
Ok(self.scratch.as_slice())
}
pub fn step(&mut self, tick: Tick, now_ms: u64) -> Result<&[Effect], CoreError> {
self.scratch.clear();
match &tick {
Tick::Inbound(_) | Tick::Command(_) | Tick::Connected(_) | Tick::Disconnected(_) => {
match self
.host
.dispatch_inbound(&tick, now_ms, &mut self.scratch)?
{
Some(idx) => {
self.register_effects_to_maps(idx, 0);
}
None => {
tracing::debug!("no module accepted tick");
}
}
return Ok(self.scratch.as_slice());
}
Tick::PortReply { corr, .. } => {
match self.corr_map.consume(corr) {
Some(module_index) => {
self.host
.dispatch_reply(module_index, &tick, now_ms, &mut self.scratch)?;
self.register_effects_to_maps(module_index, 0);
}
None => {
tracing::warn!(
corr = corr.raw(),
"received PortReply for unknown correlation, ignoring"
);
}
}
return Ok(self.scratch.as_slice());
}
Tick::PortProgress { corr, .. } => {
match self.corr_map.peek(corr) {
Some(module_index) => {
self.host
.dispatch_reply(module_index, &tick, now_ms, &mut self.scratch)?;
self.register_effects_to_maps(module_index, 0);
}
None => {
tracing::debug!(
corr = corr.raw(),
"received PortProgress for unknown correlation, ignoring"
);
}
}
return Ok(self.scratch.as_slice());
}
Tick::Timer(timer_id) => {
match self.timer_map.peek(timer_id) {
Some(module_index) => {
self.host
.dispatch_reply(module_index, &tick, now_ms, &mut self.scratch)?;
self.timer_map.remove(timer_id);
self.register_effects_to_maps(module_index, 0);
}
None => {
tracing::debug!(
id = timer_id.raw(),
"received Timer for unknown id, ignoring"
);
}
}
return Ok(self.scratch.as_slice());
}
}
}
fn register_effects_to_maps(&mut self, module_index: usize, start: usize) {
for effect in &self.scratch.as_slice()[start..] {
match effect {
Effect::Persist { corr, .. }
| Effect::PersistAtomic { corr, .. }
| Effect::Http { corr, .. }
| Effect::UploadFile { corr, .. }
| Effect::Request { corr, .. } => {
self.corr_map.register(*corr, module_index);
}
Effect::ScheduleTimer { id, .. } => {
self.timer_map.register(*id, module_index);
}
Effect::CancelTimer { id } => {
self.timer_map.remove(id);
}
_ => {}
}
}
}
}
impl Default for ExecutionShell {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
impl ExecutionShell {
pub(crate) fn timer_map_len(&self) -> usize {
self.timer_map.len()
}
}
#[cfg(test)]
#[path = "engine_tests.rs"]
mod tests;