Skip to main content

behavior/
spec.rs

1//! Intent-facing typestate composition. Every method immediately builds a
2//! concrete pure behavior; there is no separate intent representation.
3
4use std::time::Duration;
5
6use tokio::time::Instant;
7
8use crate::behavior::{Address, Behavior, BirthMode, Births};
9use crate::deadlined::{At, AtReaction};
10use crate::stashing::{StashRoute, Stashing};
11use crate::supervising::{RestartPolicy, Strategy, Supervising};
12use crate::verdict::Never;
13use crate::watching::{LinkReaction, Watching};
14use crate::{Actions, Base, Exit, Fsm, Move, SendAlgebra, State};
15
16const DEFAULT_STRATEGY: Strategy = Strategy::OneForOne;
17const DEFAULT_POLICY: RestartPolicy = RestartPolicy::Transient;
18const DEFAULT_BUDGET: (u32, Duration) = (1, Duration::from_secs(5));
19
20fn identity_nonce<N: From<u64>>(index: usize) -> N {
21    N::from(u64::try_from(index).expect("fleet index fits u64"))
22}
23
24pub struct Spec<B>(B);
25
26impl<S: State<O, Br, E>, O, Br: BirthMode, E> Spec<Base<S, O, Br, E>> {
27    #[must_use]
28    pub fn new(state: S) -> Self {
29        Self(Base::new(state))
30    }
31}
32
33impl<A, S, M, P, E> Spec<Fsm<A, S, M, P, E>>
34where
35    A: Address,
36    P: Copy + PartialEq,
37{
38    #[must_use]
39    pub fn machine(state: S, phase: P, on: fn(P, &mut S, &M) -> Result<Move<P>, E>) -> Self {
40        Self(Fsm::new(state, phase, on))
41    }
42}
43
44impl<B: Behavior> Spec<B> {
45    #[must_use]
46    pub fn from_behavior(behavior: B) -> Self {
47        Self(behavior)
48    }
49
50    #[must_use]
51    pub fn build(self) -> B {
52        self.0
53    }
54
55    #[must_use]
56    pub fn behavior(&self) -> &B {
57        &self.0
58    }
59
60    /// Observe a peer and apply a pure reaction when it stops.
61    #[must_use]
62    pub fn watch(self, peer: B::Addr, on_stopped: LinkReaction<B>) -> Spec<Watching<B>> {
63        Spec(Watching::new(self.0, peer, on_stopped))
64    }
65
66    /// Apply a pure reaction when the given absolute time is reached.
67    #[must_use]
68    pub fn at(self, when: Option<Instant>, on_reached: AtReaction<B>) -> Spec<At<B>> {
69        Spec(At::new(self.0, when, on_reached))
70    }
71
72    /// Hold messages selected by `route` and replay them on `Release`.
73    #[must_use]
74    pub fn stash(self, route: fn(&B::Msg) -> StashRoute) -> Spec<Stashing<B>>
75    where
76        B: Behavior<Ph = Never>,
77    {
78        Spec(Stashing::new(self.0, route))
79    }
80
81    /// Create a supervised child topology. Concrete proxy and monitor types
82    /// remain hidden in the returned typestate.
83    #[must_use]
84    pub fn children<C>(self, fleet: (usize, fn(usize) -> C)) -> Spec<Supervising<B, C>>
85    where
86        B: Behavior<Birth = Births<C>>,
87        C: Behavior<Ph = Never, Addr = B::Addr>,
88        <B::Addr as Address>::Nonce: From<u64>,
89    {
90        self.children_with_nonces(identity_nonce, fleet.0, fleet.1)
91    }
92
93    #[must_use]
94    pub fn children_with_nonces<C>(
95        self,
96        nonces: fn(usize) -> <B::Addr as Address>::Nonce,
97        count: usize,
98        build: fn(usize) -> C,
99    ) -> Spec<Supervising<B, C>>
100    where
101        B: Behavior<Birth = Births<C>>,
102        C: Behavior<Ph = Never, Addr = B::Addr>,
103    {
104        Spec(Supervising::new(
105            self.0,
106            nonces,
107            count,
108            build,
109            DEFAULT_STRATEGY,
110            DEFAULT_POLICY,
111            DEFAULT_BUDGET.0,
112            DEFAULT_BUDGET.1,
113        ))
114    }
115}
116
117impl<B, C> Spec<Supervising<B, C>>
118where
119    B: Behavior<Birth = Births<C>>,
120    C: Behavior<Ph = Never, Addr = B::Addr>,
121{
122    #[must_use]
123    pub fn restart(self, strategy: Strategy) -> Self {
124        Self(self.0.with_strategy(strategy))
125    }
126
127    #[must_use]
128    pub fn when(self, policy: RestartPolicy) -> Self {
129        Self(self.0.with_policy(policy))
130    }
131
132    #[must_use]
133    pub fn within(self, maximum: u32, window: Duration) -> Self {
134        Self(self.0.with_budget(maximum, window))
135    }
136}
137
138impl<B, A, Ph, Sends, Br> Behavior for Spec<B>
139where
140    A: Address + Send,
141    Sends: SendAlgebra,
142    Br: BirthMode,
143    B: Behavior<
144            Addr = A,
145            Ph = Ph,
146            Sends = Sends,
147            Birth = Br,
148            Effect = Actions<A, Ph, Sends, Br>,
149            Done = Exit<A>,
150        > + Send,
151    A::Nonce: Send,
152    B::Msg: Send,
153    B::Event: Send,
154{
155    type Addr = A;
156    type Msg = B::Msg;
157    type Event = B::Event;
158    type Sends = Sends;
159    type Ph = Ph;
160    type Error = B::Error;
161    type Birth = Br;
162    type Effect = B::Effect;
163    type Done = B::Done;
164
165    async fn init(&mut self) -> Result<Self::Effect, B::Error> {
166        self.0.init().await
167    }
168
169    async fn step(&mut self, event: B::Event) -> Result<Self::Effect, B::Error> {
170        self.0.step(event).await
171    }
172}