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, AtId, 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> {
25    behavior: B,
26    next_timer: u64,
27}
28
29impl<S: State<O, Br, E>, O, Br: BirthMode, E> Spec<Base<S, O, Br, E>> {
30    #[must_use]
31    pub fn new(state: S) -> Self {
32        Self {
33            behavior: Base::new(state),
34            next_timer: 0,
35        }
36    }
37}
38
39impl<A, S, M, P, E> Spec<Fsm<A, S, M, P, E>>
40where
41    A: Address,
42    P: Copy + PartialEq,
43{
44    #[must_use]
45    pub fn machine(state: S, phase: P, on: fn(P, &mut S, &M) -> Result<Move<P>, E>) -> Self {
46        Self {
47            behavior: Fsm::new(state, phase, on),
48            next_timer: 0,
49        }
50    }
51}
52
53impl<B: Behavior> Spec<B> {
54    #[must_use]
55    pub fn from_behavior(behavior: B) -> Self {
56        Self {
57            behavior,
58            next_timer: 0,
59        }
60    }
61
62    #[must_use]
63    pub fn build(self) -> B {
64        self.behavior
65    }
66
67    #[must_use]
68    pub fn behavior(&self) -> &B {
69        &self.behavior
70    }
71
72    /// Observe a peer and apply a pure reaction when it stops.
73    #[must_use]
74    pub fn watch(self, peer: B::Addr, on_stopped: LinkReaction<B>) -> Spec<Watching<B>> {
75        Spec {
76            behavior: Watching::new(self.behavior, peer, on_stopped),
77            next_timer: self.next_timer,
78        }
79    }
80
81    /// Apply a pure reaction when the given absolute time is reached.
82    ///
83    /// # Panics
84    ///
85    /// Panics if one specification composes more than `u64::MAX` timer
86    /// capabilities.
87    #[must_use]
88    pub fn at(self, when: Option<Instant>, on_reached: AtReaction<B>) -> Spec<At<B>> {
89        Spec {
90            behavior: At::new(self.behavior, AtId(self.next_timer), when, on_reached),
91            next_timer: self
92                .next_timer
93                .checked_add(1)
94                .expect("timer identity exhausted"),
95        }
96    }
97
98    /// Hold messages selected by `route` and replay them on `Release`.
99    #[must_use]
100    pub fn stash(self, route: fn(&B::Msg) -> StashRoute) -> Spec<Stashing<B>>
101    where
102        B: Behavior<Ph = Never>,
103    {
104        Spec {
105            behavior: Stashing::new(self.behavior, route),
106            next_timer: self.next_timer,
107        }
108    }
109
110    /// Create a supervised child topology. Concrete proxy and monitor types
111    /// remain hidden in the returned typestate.
112    #[must_use]
113    pub fn children<C>(self, fleet: (usize, fn(usize) -> C)) -> Spec<Supervising<B, C>>
114    where
115        B: Behavior<Birth = Births<C>>,
116        C: Behavior<Ph = Never, Addr = B::Addr>,
117        <B::Addr as Address>::Nonce: From<u64>,
118    {
119        self.children_with_nonces(identity_nonce, fleet.0, fleet.1)
120    }
121
122    #[must_use]
123    pub fn children_with_nonces<C>(
124        self,
125        nonces: fn(usize) -> <B::Addr as Address>::Nonce,
126        count: usize,
127        build: fn(usize) -> C,
128    ) -> Spec<Supervising<B, C>>
129    where
130        B: Behavior<Birth = Births<C>>,
131        C: Behavior<Ph = Never, Addr = B::Addr>,
132    {
133        Spec {
134            behavior: Supervising::new(
135                self.behavior,
136                nonces,
137                count,
138                build,
139                DEFAULT_STRATEGY,
140                DEFAULT_POLICY,
141                DEFAULT_BUDGET.0,
142                DEFAULT_BUDGET.1,
143            ),
144            next_timer: self.next_timer,
145        }
146    }
147}
148
149impl<B, C> Spec<Supervising<B, C>>
150where
151    B: Behavior<Birth = Births<C>>,
152    C: Behavior<Ph = Never, Addr = B::Addr>,
153{
154    #[must_use]
155    pub fn restart(self, strategy: Strategy) -> Self {
156        Self {
157            behavior: self.behavior.with_strategy(strategy),
158            next_timer: self.next_timer,
159        }
160    }
161
162    #[must_use]
163    pub fn when(self, policy: RestartPolicy) -> Self {
164        Self {
165            behavior: self.behavior.with_policy(policy),
166            next_timer: self.next_timer,
167        }
168    }
169
170    #[must_use]
171    pub fn within(self, maximum: u32, window: Duration) -> Self {
172        Self {
173            behavior: self.behavior.with_budget(maximum, window),
174            next_timer: self.next_timer,
175        }
176    }
177}
178
179impl<B, A, Ph, Sends, Br> Behavior for Spec<B>
180where
181    A: Address + Send,
182    Sends: SendAlgebra,
183    Br: BirthMode,
184    B: Behavior<
185            Addr = A,
186            Ph = Ph,
187            Sends = Sends,
188            Birth = Br,
189            Effect = Actions<A, Ph, Sends, Br>,
190            Done = Exit<A>,
191        > + Send,
192    A::Nonce: Send,
193    B::Msg: Send,
194    B::Event: Send,
195{
196    type Addr = A;
197    type Msg = B::Msg;
198    type Event = B::Event;
199    type Sends = Sends;
200    type Ph = Ph;
201    type Error = B::Error;
202    type Birth = Br;
203    type Effect = B::Effect;
204    type Done = B::Done;
205
206    async fn init(&mut self) -> Result<Self::Effect, B::Error> {
207        self.behavior.init().await
208    }
209
210    async fn step(&mut self, event: B::Event) -> Result<Self::Effect, B::Error> {
211        self.behavior.step(event).await
212    }
213}