use std::{any::Any, marker::PhantomData};
use crate::synca::Entry;
use crate::valtron::AnyResult;
use crate::valtron::{
BoxedExecutionEngine, BoxedPanicHandler, DoNext, ExecutionAction, ExecutorError, FnMutReady,
FnReady, OnNext, SpawnInfo, TaskIterator, TaskReadyResolver, TaskStatus,
};
#[cfg(feature = "multi")]
mod sendable;
#[cfg(not(feature = "multi"))]
mod non_sendable;
#[derive(Debug, Clone, Copy)]
pub enum LocalFallback {
Lift,
Sequence,
}
pub struct TaskSpawnConfig<Done, Pending, Action, Resolver, Task>
where
Action: ExecutionAction,
Resolver: TaskReadyResolver<Action, Done, Pending>,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action>,
{
pub(crate) engine: BoxedExecutionEngine,
pub(crate) task: Option<Task>,
pub(crate) parent: Option<Entry>,
pub(crate) resolver: Option<Resolver>,
pub(crate) panic_handler: Option<BoxedPanicHandler>,
pub(crate) _marker: PhantomData<(Done, Pending, Action)>,
}
impl<Done, Pending, Action, Resolver, Task> TaskSpawnConfig<Done, Pending, Action, Resolver, Task>
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Resolver: TaskReadyResolver<Action, Done, Pending> + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
{
#[must_use]
pub fn new(engine: BoxedExecutionEngine) -> Self {
Self {
engine,
task: None,
parent: None,
resolver: None,
panic_handler: None,
_marker: PhantomData,
}
}
#[must_use]
pub fn with_task(mut self, task: Task) -> Self {
self.task = Some(task);
self
}
#[must_use]
pub fn with_parent(mut self, parent: Entry) -> Self {
self.parent = Some(parent);
self
}
#[must_use]
pub fn maybe_parent(mut self, parent: Option<Entry>) -> Self {
self.parent = parent;
self
}
#[must_use]
pub fn with_resolver(mut self, resolver: Resolver) -> Self {
self.resolver = Some(resolver);
self
}
#[must_use]
pub fn with_panic_handler<T>(mut self, handler: T) -> Self
where
T: Fn(Box<dyn Any + Send>) + Send + Sync + 'static,
{
self.panic_handler = Some(Box::new(handler));
self
}
#[must_use]
pub fn as_scheduled(self) -> ScheduleBuilder<Done, Pending, Action, Resolver, Task> {
ScheduleBuilder { config: self }
}
#[must_use]
pub fn as_lifted(self) -> LiftBuilder<Done, Pending, Action, Resolver, Task> {
LiftBuilder { config: self }
}
#[must_use]
pub fn as_sequenced(
mut self,
parent: Entry,
) -> SequenceBuilder<Done, Pending, Action, Resolver, Task> {
self.parent = Some(parent);
SequenceBuilder { config: self }
}
#[must_use]
pub fn as_broadcast(self) -> BroadcastBuilder<Done, Pending, Action, Resolver, Task> {
BroadcastBuilder {
config: self,
fallback: LocalFallback::Lift,
}
}
#[must_use]
pub fn as_broadcast_or_lift(self) -> BroadcastBuilder<Done, Pending, Action, Resolver, Task> {
BroadcastBuilder {
config: self,
fallback: LocalFallback::Lift,
}
}
#[must_use]
pub fn as_broadcast_or_sequenced(
mut self,
parent: Entry,
) -> BroadcastBuilder<Done, Pending, Action, Resolver, Task> {
self.parent = Some(parent);
BroadcastBuilder {
config: self,
fallback: LocalFallback::Sequence,
}
}
}
impl<F, Done, Pending, Action, Task>
TaskSpawnConfig<Done, Pending, Action, FnReady<F, Action>, Task>
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
F: Fn(TaskStatus<Done, Pending, Action>, BoxedExecutionEngine) + 'static,
{
#[must_use]
pub fn on_next(self, action: F) -> Self {
self.with_resolver(FnReady::new(action))
}
}
impl<F, Done, Pending, Action, Task>
TaskSpawnConfig<Done, Pending, Action, FnMutReady<F, Action>, Task>
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
F: FnMut(TaskStatus<Done, Pending, Action>, BoxedExecutionEngine) + 'static,
{
#[must_use]
pub fn on_next_mut(self, action: F) -> Self
where
F: Fn(TaskStatus<Done, Pending, Action>, BoxedExecutionEngine) + 'static,
{
self.with_resolver(FnMutReady::new(action))
}
}
impl<F, Done, Pending, Action, Task>
TaskSpawnConfig<Done, Pending, Action, FnReady<F, Action>, Task>
where
Done: Send + 'static,
Pending: Send + 'static,
Action: ExecutionAction + Send + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + Send + 'static,
F: Fn(TaskStatus<Done, Pending, Action>, BoxedExecutionEngine) + Send + 'static,
{
#[must_use]
pub fn on_send_next(self, action: F) -> Self {
self.with_resolver(FnReady::new(action))
}
}
impl<F, Done, Pending, Action, Task>
TaskSpawnConfig<Done, Pending, Action, FnMutReady<F, Action>, Task>
where
Done: Send + 'static,
Pending: Send + 'static,
Action: ExecutionAction + Send + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + Send + 'static,
F: FnMut(TaskStatus<Done, Pending, Action>, BoxedExecutionEngine) + Send + 'static,
{
#[must_use]
pub fn on_send_next_mut(self, action: F) -> Self
where
F: Fn(TaskStatus<Done, Pending, Action>, BoxedExecutionEngine) + 'static,
{
self.with_resolver(FnMutReady::new(action))
}
}
pub struct ScheduleBuilder<Done, Pending, Action, Resolver, Task>
where
Action: ExecutionAction,
Resolver: TaskReadyResolver<Action, Done, Pending>,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action>,
{
pub(crate) config: TaskSpawnConfig<Done, Pending, Action, Resolver, Task>,
}
pub struct LiftBuilder<Done, Pending, Action, Resolver, Task>
where
Action: ExecutionAction,
Resolver: TaskReadyResolver<Action, Done, Pending>,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action>,
{
pub(crate) config: TaskSpawnConfig<Done, Pending, Action, Resolver, Task>,
}
pub struct SequenceBuilder<Done, Pending, Action, Resolver, Task>
where
Action: ExecutionAction,
Resolver: TaskReadyResolver<Action, Done, Pending>,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action>,
{
pub(crate) config: TaskSpawnConfig<Done, Pending, Action, Resolver, Task>,
}
pub struct BroadcastBuilder<Done, Pending, Action, Resolver, Task>
where
Action: ExecutionAction,
Resolver: TaskReadyResolver<Action, Done, Pending>,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action>,
{
pub(crate) config: TaskSpawnConfig<Done, Pending, Action, Resolver, Task>,
pub(crate) fallback: LocalFallback,
}
fn local_spawn_task<Done, Pending, Action, Resolver, Task>(
task: Task,
resolver: Option<Resolver>,
) -> crate::valtron::BoxedExecutionIterator
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Resolver: TaskReadyResolver<Action, Done, Pending> + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
{
match resolver {
Some(resolver) => OnNext::new(task, resolver).into(),
None => Box::new(DoNext::new(task)),
}
}
impl<Done, Pending, Action, Resolver, Task>
ScheduleBuilder<Done, Pending, Action, Resolver, Task>
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Resolver: TaskReadyResolver<Action, Done, Pending> + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
{
pub fn spawn(self) -> AnyResult<SpawnInfo, ExecutorError> {
let c = self.config;
let Some(task) = c.task else {
return Err(ExecutorError::TaskRequired);
};
c.engine.schedule(local_spawn_task(task, c.resolver))
}
}
impl<Done, Pending, Action, Resolver, Task> LiftBuilder<Done, Pending, Action, Resolver, Task>
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Resolver: TaskReadyResolver<Action, Done, Pending> + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
{
pub fn spawn(self) -> AnyResult<SpawnInfo, ExecutorError> {
let c = self.config;
let parent = c.parent;
let Some(task) = c.task else {
return Err(ExecutorError::TaskRequired);
};
c.engine.lift(local_spawn_task(task, c.resolver), parent)
}
}
impl<Done, Pending, Action, Resolver, Task>
SequenceBuilder<Done, Pending, Action, Resolver, Task>
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Resolver: TaskReadyResolver<Action, Done, Pending> + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
{
pub fn spawn(self) -> AnyResult<SpawnInfo, ExecutorError> {
let c = self.config;
let Some(parent) = c.parent else {
return Err(ExecutorError::ParentMustBeSupplied);
};
let Some(task) = c.task else {
return Err(ExecutorError::TaskRequired);
};
c.engine
.sequenced(local_spawn_task(task, c.resolver), parent)
}
}
#[must_use]
#[allow(clippy::type_complexity)]
pub fn spawn_builder<Task, Action>(
engine: BoxedExecutionEngine,
) -> TaskSpawnConfig<
Task::Ready,
Task::Pending,
Task::Spawner,
Box<dyn TaskReadyResolver<Task::Spawner, Task::Ready, Task::Pending> + 'static>,
Task,
>
where
Task::Ready: 'static,
Task::Pending: 'static,
Task: TaskIterator<Spawner = Action> + 'static,
Action: ExecutionAction + 'static,
{
TaskSpawnConfig::new(engine)
}
#[must_use]
#[allow(clippy::type_complexity)]
pub fn spawn_broadcaster<Task, Action>(
engine: BoxedExecutionEngine,
) -> TaskSpawnConfig<
Task::Ready,
Task::Pending,
Task::Spawner,
Box<dyn TaskReadyResolver<Task::Spawner, Task::Ready, Task::Pending> + Send + 'static>,
Task,
>
where
Task::Ready: Send + 'static,
Task::Pending: Send + 'static,
Task: TaskIterator<Spawner = Action> + Send + 'static,
Action: ExecutionAction + Send + 'static,
{
TaskSpawnConfig::new(engine)
}