use behavior::{Actions, Address, Behavior, BirthMode, Exit, Never, SendAlgebra, Step};
use bombay_machine_executor::{ExclusiveExecutor, ExclusiveState};
use crate::{BehaviorMachine, Environment, RunError, RunExit};
pub struct RuntimeEffects<A: Address, Sends, Birth: BirthMode> {
pub sends: Sends,
pub creates: Vec<behavior::Create<A, Birth::Child>>,
}
async fn interpret<A, Sends, Birth, E>(
actions: Actions<A, Never, Sends, Birth>,
environment: &mut E,
) -> Result<Option<Exit<A>>, E::Error>
where
A: Address,
Sends: SendAlgebra,
Birth: BirthMode,
E: Environment<Effect = RuntimeEffects<A, Sends, Birth>>,
{
let Actions {
sends,
creates,
become_,
} = actions;
environment
.interpret(RuntimeEffects { sends, creates })
.await?;
Ok(match become_ {
Step::Continue => None,
Step::Goto(never) => match never {},
Step::Stop(exit) => Some(exit),
})
}
enum State<B: Behavior> {
Uninitialized(B),
Running(ExclusiveExecutor<BehaviorMachine<B>>),
Terminated,
Retired,
}
pub struct Driver<B: Behavior, E> {
state: State<B>,
environment: E,
}
impl<B, E> Driver<B, E>
where
B: Behavior,
{
pub fn new(behavior: B, environment: E) -> Self {
Self {
state: State::Uninitialized(behavior),
environment,
}
}
}
impl<B, E> Driver<B, E>
where
B: Behavior<Ph = Never> + Send,
B::Event: Send,
E: Environment<Event = B::Event, Effect = RuntimeEffects<B::Addr, B::Sends, B::Birth>>,
{
pub async fn run_init(
&mut self,
) -> Result<Option<Exit<B::Addr>>, RunError<B::Error, E::Error>> {
let State::Uninitialized(behavior) = std::mem::replace(&mut self.state, State::Retired)
else {
panic!("run_init called on non-uninitialized driver");
};
let mut machine = BehaviorMachine::for_runtime(behavior);
let initial = machine.behavior_mut().init().map_err(RunError::Behavior)?;
let exit = interpret(initial, &mut self.environment)
.await
.map_err(RunError::Environment)?;
self.state = if exit.is_some() {
State::Terminated
} else {
State::Running(ExclusiveExecutor::new(machine))
};
Ok(exit)
}
pub async fn run_loop(
&mut self,
) -> Result<RunExit<Exit<B::Addr>>, RunError<B::Error, E::Error>> {
loop {
let poisoned = match &self.state {
State::Running(executor) => executor.state() == ExclusiveState::Poisoned,
_ => panic!("run_loop on non-running driver"),
};
if poisoned {
self.environment.retire().await;
self.state = State::Retired;
return Err(RunError::Poisoned);
}
let Some(event) = self.environment.next().await else {
self.state = State::Terminated;
return Ok(RunExit::EnvironmentClosed);
};
let output = match &mut self.state {
State::Running(executor) => executor.turn(event),
_ => panic!("run_loop on non-running driver"),
};
let actions = match output {
Ok(Ok(actions)) => actions,
Ok(Err(error)) => {
self.state = State::Terminated;
return Err(RunError::Behavior(error));
}
Err(_poisoned) => {
self.environment.retire().await;
self.state = State::Retired;
return Err(RunError::Poisoned);
}
};
match interpret(actions, &mut self.environment).await {
Ok(Some(exit)) => {
self.state = State::Terminated;
return Ok(RunExit::Stopped(exit));
}
Ok(None) => {}
Err(error) => {
self.state = State::Terminated;
return Err(RunError::Environment(error));
}
}
}
}
pub async fn retire(&mut self) {
self.environment.retire().await;
self.state = State::Retired;
}
pub async fn run(&mut self) -> Result<RunExit<Exit<B::Addr>>, RunError<B::Error, E::Error>> {
let result = async {
if let Some(exit) = self.run_init().await? {
return Ok(RunExit::Stopped(exit));
}
self.run_loop().await
}
.await;
self.environment.retire().await;
self.state = State::Retired;
result
}
}