use std::time::Duration;
use std::time::Instant;
use crate::Actions;
use crate::BehaviorBase;
use crate::behavior::{Address, Behavior, Births};
use crate::calculus::{BehaviorActed, EventInput, UserEvent, delegate_transition};
use crate::next::Never;
use crate::protocol::TimerId;
use crate::shutdown::{FinalizeOnShutdown, ShutdownReaction, StopOnShutdown};
use crate::stash::{Stash, StashRoute};
use crate::supervision::{RestartPolicy, Strategy, SupervisionFailureReaction, Supervisor};
use crate::timing::{Deadline, DeadlineReaction};
use crate::timing::{ReceiveTimeout, ReceiveTimeoutReaction};
use crate::watch::{LinkReaction, Watch};
const DEFAULT_STRATEGY: Strategy = Strategy::OneForOne;
const DEFAULT_POLICY: RestartPolicy = RestartPolicy::Transient;
const DEFAULT_BUDGET: (u32, Duration) = (1, Duration::from_secs(5));
pub struct Compose<B> {
behavior: B,
}
pub struct Initialized<B: Behavior> {
pub behavior: Active<B>,
pub actions: Actions<B::Addr, B::Ph, B::Sends, B::Birth>,
}
pub struct Active<B: Behavior> {
pub(crate) behavior: B,
}
impl<B: Behavior> Active<B> {
#[must_use]
pub fn base(&self) -> &B::Base
where
B: BehaviorBase,
{
self.behavior.base()
}
#[must_use]
pub fn stashed(&self) -> usize
where
B: crate::StashStatus,
{
self.behavior.stashed_messages()
}
pub fn transition(&mut self, event: B::Event) -> BehaviorActed<B> {
delegate_transition(&mut self.behavior, event)
}
pub fn receive(&mut self, from: B::Addr, message: B::Msg) -> BehaviorActed<B> {
self.transition(B::Event::user(from, message))
}
pub fn on<Input>(&mut self, input: Input) -> BehaviorActed<B>
where
B::Event: EventInput<Input>,
{
self.transition(B::Event::inject(input))
}
}
impl<B: Behavior> core::ops::Deref for Active<B> {
type Target = B;
fn deref(&self) -> &Self::Target {
&self.behavior
}
}
impl<B> Compose<B> {
#[must_use]
pub const fn new(behavior: B) -> Self {
Self { behavior }
}
}
impl<B: Behavior> Compose<B> {
fn map_behavior<Mapped>(self, map: impl FnOnce(B) -> Mapped) -> Compose<Mapped> {
Compose {
behavior: map(self.behavior),
}
}
fn try_map_behavior<Mapped, E>(
self,
map: impl FnOnce(B) -> Result<Mapped, E>,
) -> Result<Compose<Mapped>, E> {
map(self.behavior).map(|behavior| Compose { behavior })
}
#[must_use]
pub fn base(&self) -> &B::Base
where
B: BehaviorBase,
{
self.behavior.base()
}
#[must_use]
pub fn definition(&self) -> &B {
&self.behavior
}
pub fn initialize(self) -> Result<Initialized<B>, B::Error> {
let mut behavior = self.behavior;
let actions = crate::calculus::initialize(&mut behavior)?;
Ok(Initialized {
behavior: Active { behavior },
actions,
})
}
#[must_use]
pub fn stop_on_shutdown(self) -> Compose<StopOnShutdown<B>> {
self.map_behavior(StopOnShutdown::new)
}
#[must_use]
pub fn finalize_on_shutdown(
self,
finalize: ShutdownReaction<B>,
) -> Compose<FinalizeOnShutdown<B>> {
self.map_behavior(|behavior| FinalizeOnShutdown::new(behavior, finalize))
}
#[must_use]
pub fn watch(self, peer: B::Addr, on_stopped: LinkReaction<B>) -> Compose<Watch<B>> {
self.map_behavior(|behavior| Watch::new(behavior, peer, on_stopped))
}
#[must_use]
pub fn deadline(
self,
timer: TimerId,
when: Option<Instant>,
on_reached: DeadlineReaction<B>,
) -> Compose<Deadline<B>> {
self.map_behavior(|behavior| Deadline::new(behavior, timer, when, on_reached))
}
#[must_use]
pub fn receive_timeout(
self,
timer: TimerId,
after: Duration,
on_elapsed: ReceiveTimeoutReaction<B>,
) -> Compose<ReceiveTimeout<B>> {
self.map_behavior(|behavior| ReceiveTimeout::new(behavior, timer, after, on_elapsed))
}
#[must_use]
pub fn stash(self, route: fn(&B::Msg) -> StashRoute) -> Compose<Stash<B>>
where
B: Behavior<Ph = Never>,
{
self.map_behavior(|behavior| Stash::new(behavior, route))
}
#[must_use]
pub fn children<C>(
self,
nonces: fn(usize) -> <B::Addr as Address>::Nonce,
count: usize,
build: fn(usize) -> Option<C>,
) -> Result<Compose<Supervisor<B, C>>, crate::FleetError<<B::Addr as Address>::Nonce>>
where
B: Behavior<Birth = Births<C>>,
C: Behavior<Ph = Never, Addr = B::Addr>,
<B::Addr as Address>::Nonce: From<u64>,
{
self.try_map_behavior(|behavior| {
Supervisor::new(
behavior,
nonces,
count,
build,
DEFAULT_STRATEGY,
DEFAULT_POLICY,
DEFAULT_BUDGET.0,
DEFAULT_BUDGET.1,
)
})
}
}
impl<B, C> Compose<Supervisor<B, C>>
where
B: Behavior<Birth = Births<C>>,
C: Behavior<Ph = Never, Addr = B::Addr>,
<B::Addr as Address>::Nonce: From<u64>,
{
#[must_use]
pub fn restart(self, strategy: Strategy) -> Self {
Self {
behavior: self.behavior.with_strategy(strategy),
}
}
#[must_use]
pub fn when(self, policy: RestartPolicy) -> Self {
Self {
behavior: self.behavior.with_policy(policy),
}
}
#[must_use]
pub fn within(self, maximum: u32, window: Duration) -> Self {
Self {
behavior: self.behavior.with_budget(maximum, window),
}
}
#[must_use]
pub fn on_supervision_failure(self, reaction: SupervisionFailureReaction<B>) -> Self {
Self {
behavior: self.behavior.with_failure_reaction(reaction),
}
}
}