Skip to main content

sark_core/decode/state/
mod.rs

1use core::{convert::Infallible, marker::PhantomData, ops::ControlFlow};
2
3use crate::{
4    body::admit::{Finished, Open, Track},
5    decode::{exec, request},
6};
7
8pub mod admit;
9pub mod body;
10pub mod open;
11mod uniform;
12
13pub use uniform::Uniform;
14
15pub struct Uniforms<P>(PhantomData<fn() -> P>);
16
17/// Payload family carried by each route state.
18pub trait Set {
19    type SyncOpen;
20    type SyncFinished;
21    type TaskOpen;
22    type TaskFinished;
23    type DuplexOpen;
24    type DuplexFinished;
25    type UnsupportedOpen;
26    type UnsupportedFinished;
27}
28
29/// Route selected before its guard runs.
30pub enum Routed {}
31
32impl Set for Routed {
33    type SyncOpen = Track<Open>;
34    type SyncFinished = Track<Finished>;
35    type TaskOpen = Track<Open>;
36    type TaskFinished = Track<Finished>;
37    type DuplexOpen = ();
38    type DuplexFinished = ();
39    type UnsupportedOpen = Track<Open>;
40    type UnsupportedFinished = Track<Finished>;
41}
42
43/// Route selected after its guard admits it.
44pub enum Admitted {}
45
46impl Set for Admitted {
47    type SyncOpen = Track<Open>;
48    type SyncFinished = Track<Finished>;
49    type TaskOpen = Track<Open>;
50    type TaskFinished = Track<Finished>;
51    type DuplexOpen = ();
52    type DuplexFinished = ();
53    type UnsupportedOpen = Track<Open>;
54    type UnsupportedFinished = Track<Finished>;
55}
56
57/// Payload conversion performed when transport input reaches its terminal edge.
58///
59/// The generated route family owns the only runtime discriminant. Implementors
60/// move the payload without allocating or introducing a second phase tag.
61pub trait Finish: Set {
62    fn sync(body: Self::SyncOpen) -> Self::SyncFinished;
63
64    fn task(body: Self::TaskOpen) -> Self::TaskFinished;
65
66    fn duplex(body: Self::DuplexOpen) -> Self::DuplexFinished;
67
68    fn reject(body: Self::UnsupportedOpen) -> Self::UnsupportedFinished;
69}
70
71/// One tagless route payload belonging to an aggregate prepared request.
72pub trait Slot<P>: Sized {
73    fn prepare(self) -> P;
74
75    fn admit<K, B>(self, body: B) -> request::Admitted<K, P, B> {
76        request::Admitted::new(self.prepare(), body)
77    }
78}
79
80pub struct Whole<P>(P);
81
82impl<P> Whole<P> {
83    pub const fn new(prep: P) -> Self {
84        Self(prep)
85    }
86}
87
88impl<P> Slot<P> for Whole<P> {
89    fn prepare(self) -> P {
90        self.0
91    }
92}
93
94/// Consuming route-state transformation.
95pub trait Map<P, F: Set, G: Set>: Sized {
96    type Break;
97    type Retained;
98
99    fn sync_open<S: Slot<P>>(
100        self,
101        slot: S,
102        body: F::SyncOpen,
103    ) -> ControlFlow<Self::Break, (Self::Retained, S, G::SyncOpen)>;
104
105    fn sync_done<S: Slot<P>>(
106        self,
107        slot: S,
108        body: F::SyncFinished,
109    ) -> ControlFlow<Self::Break, (Self::Retained, S, G::SyncFinished)>;
110
111    fn task_open<S: Slot<P>>(
112        self,
113        slot: S,
114        body: F::TaskOpen,
115    ) -> ControlFlow<Self::Break, (Self::Retained, S, G::TaskOpen)>;
116
117    fn task_done<S: Slot<P>>(
118        self,
119        slot: S,
120        body: F::TaskFinished,
121    ) -> ControlFlow<Self::Break, (Self::Retained, S, G::TaskFinished)>;
122
123    fn duplex_open<S: Slot<P>>(
124        self,
125        slot: S,
126        body: F::DuplexOpen,
127    ) -> ControlFlow<Self::Break, (Self::Retained, S, G::DuplexOpen)>;
128
129    fn duplex_done<S: Slot<P>>(
130        self,
131        slot: S,
132        body: F::DuplexFinished,
133    ) -> ControlFlow<Self::Break, (Self::Retained, S, G::DuplexFinished)>;
134
135    fn reject_open<S: Slot<P>>(
136        self,
137        slot: S,
138        body: F::UnsupportedOpen,
139    ) -> ControlFlow<Self::Break, (Self::Retained, S, G::UnsupportedOpen)>;
140
141    fn reject_done<S: Slot<P>>(
142        self,
143        slot: S,
144        body: F::UnsupportedFinished,
145    ) -> ControlFlow<Self::Break, (Self::Retained, S, G::UnsupportedFinished)>;
146}
147
148/// Zero-state payload-preserving transition.
149pub struct Pass;
150
151impl<P, F, G> Map<P, F, G> for Pass
152where
153    F: Set,
154    G: Set<
155            SyncOpen = F::SyncOpen,
156            SyncFinished = F::SyncFinished,
157            TaskOpen = F::TaskOpen,
158            TaskFinished = F::TaskFinished,
159            DuplexOpen = F::DuplexOpen,
160            DuplexFinished = F::DuplexFinished,
161            UnsupportedOpen = F::UnsupportedOpen,
162            UnsupportedFinished = F::UnsupportedFinished,
163        >,
164{
165    type Break = Infallible;
166    type Retained = ();
167
168    fn sync_open<S: Slot<P>>(
169        self,
170        slot: S,
171        body: F::SyncOpen,
172    ) -> ControlFlow<Infallible, ((), S, F::SyncOpen)> {
173        ControlFlow::Continue(((), slot, body))
174    }
175
176    fn sync_done<S: Slot<P>>(
177        self,
178        slot: S,
179        body: F::SyncFinished,
180    ) -> ControlFlow<Infallible, ((), S, F::SyncFinished)> {
181        ControlFlow::Continue(((), slot, body))
182    }
183
184    fn task_open<S: Slot<P>>(
185        self,
186        slot: S,
187        body: F::TaskOpen,
188    ) -> ControlFlow<Infallible, ((), S, F::TaskOpen)> {
189        ControlFlow::Continue(((), slot, body))
190    }
191
192    fn task_done<S: Slot<P>>(
193        self,
194        slot: S,
195        body: F::TaskFinished,
196    ) -> ControlFlow<Infallible, ((), S, F::TaskFinished)> {
197        ControlFlow::Continue(((), slot, body))
198    }
199
200    fn duplex_open<S: Slot<P>>(
201        self,
202        slot: S,
203        body: F::DuplexOpen,
204    ) -> ControlFlow<Infallible, ((), S, F::DuplexOpen)> {
205        ControlFlow::Continue(((), slot, body))
206    }
207
208    fn duplex_done<S: Slot<P>>(
209        self,
210        slot: S,
211        body: F::DuplexFinished,
212    ) -> ControlFlow<Infallible, ((), S, F::DuplexFinished)> {
213        ControlFlow::Continue(((), slot, body))
214    }
215
216    fn reject_open<S: Slot<P>>(
217        self,
218        slot: S,
219        body: F::UnsupportedOpen,
220    ) -> ControlFlow<Infallible, ((), S, F::UnsupportedOpen)> {
221        ControlFlow::Continue(((), slot, body))
222    }
223
224    fn reject_done<S: Slot<P>>(
225        self,
226        slot: S,
227        body: F::UnsupportedFinished,
228    ) -> ControlFlow<Infallible, ((), S, F::UnsupportedFinished)> {
229        ControlFlow::Continue(((), slot, body))
230    }
231}
232
233/// Borrowed operation over one selected route state.
234pub trait Each<P, F: Set>: Sized {
235    type Output<'a>;
236
237    fn sync_open<'a, S: Slot<P>>(self, slot: &S, body: &'a mut F::SyncOpen) -> Self::Output<'a>;
238
239    fn sync_done<'a, S: Slot<P>>(self, slot: &S, body: &'a mut F::SyncFinished)
240    -> Self::Output<'a>;
241
242    fn task_open<'a, S: Slot<P>>(self, slot: &S, body: &'a mut F::TaskOpen) -> Self::Output<'a>;
243
244    fn task_done<'a, S: Slot<P>>(self, slot: &S, body: &'a mut F::TaskFinished)
245    -> Self::Output<'a>;
246
247    fn duplex_open<'a, S: Slot<P>>(self, slot: &S, body: &'a mut F::DuplexOpen)
248    -> Self::Output<'a>;
249
250    fn duplex_done<'a, S: Slot<P>>(
251        self,
252        slot: &S,
253        body: &'a mut F::DuplexFinished,
254    ) -> Self::Output<'a>;
255
256    fn reject_open<'a, S: Slot<P>>(
257        self,
258        slot: &S,
259        body: &'a mut F::UnsupportedOpen,
260    ) -> Self::Output<'a>;
261
262    fn reject_done<'a, S: Slot<P>>(
263        self,
264        slot: &S,
265        body: &'a mut F::UnsupportedFinished,
266    ) -> Self::Output<'a>;
267}
268
269/// Terminal consumption of one selected route state.
270///
271/// Unlike [`Map`], a fold cannot produce another route state and therefore
272/// needs neither an uninhabited payload family nor a `ControlFlow` fallback.
273pub trait Fold<P, F: Set>: Sized {
274    type Output;
275
276    fn sync_open<S: Slot<P>>(self, slot: S, body: F::SyncOpen) -> Self::Output;
277
278    fn sync_done<S: Slot<P>>(self, slot: S, body: F::SyncFinished) -> Self::Output;
279
280    fn task_open<S: Slot<P>>(self, slot: S, body: F::TaskOpen) -> Self::Output;
281
282    fn task_done<S: Slot<P>>(self, slot: S, body: F::TaskFinished) -> Self::Output;
283
284    fn duplex_open<S: Slot<P>>(self, slot: S, body: F::DuplexOpen) -> Self::Output;
285
286    fn duplex_done<S: Slot<P>>(self, slot: S, body: F::DuplexFinished) -> Self::Output;
287
288    fn reject_open<S: Slot<P>>(self, slot: S, body: F::UnsupportedOpen) -> Self::Output;
289
290    fn reject_done<S: Slot<P>>(self, slot: S, body: F::UnsupportedFinished) -> Self::Output;
291}
292
293/// One type constructor for every state of the same generated route enum.
294pub trait Family<P>: Sized {
295    type State<F: Set>: Flow<P, F, Family = Self> + body::Flow<F>;
296}
297
298/// A route family whose every arm has the same execution kind.
299///
300/// Generated homogeneous routers implement this capability directly, so a
301/// protocol restricted to one execution kind never needs mismatch arms.
302pub trait Homogeneous<P, K: exec::Kind>: Family<P> {
303    fn fold<F, T>(state: Self::State<F>, fold: T) -> T::Output
304    where
305        F: Set,
306        T: exec::Fold<P, F, K>;
307}
308
309impl<P> Family<P> for Uniforms<P> {
310    type State<F: Set> = Uniform<P, F>;
311}
312
313impl<P> Homogeneous<P, exec::Sync> for Uniforms<P> {
314    fn fold<F, T>(state: Uniform<P, F>, fold: T) -> T::Output
315    where
316        F: Set,
317        T: exec::Fold<P, F, exec::Sync>,
318    {
319        let (prep, body) = state.into_parts();
320        match body {
321            request::Body::Open(body) => fold.open(Whole::new(prep), body),
322            request::Body::Finished(body) => fold.finished(Whole::new(prep), body),
323        }
324    }
325}
326
327type Mapped<B, R, S, P, G> = ControlFlow<B, (R, <S as Family<P>>::State<G>)>;
328
329/// A generated route enum carrying exactly one route discriminant.
330pub trait Flow<P, F: Set>: Sized {
331    type Family: Family<P>;
332
333    fn each<'a, M: Each<P, F>>(&'a mut self, each: M) -> M::Output<'a>;
334
335    fn map_retain<G: Set, M: Map<P, F, G>>(
336        self,
337        map: M,
338    ) -> Mapped<M::Break, M::Retained, Self::Family, P, G>;
339
340    fn map<G: Set, M: Map<P, F, G, Retained = ()>>(
341        self,
342        map: M,
343    ) -> ControlFlow<M::Break, <Self::Family as Family<P>>::State<G>> {
344        self.map_retain(map).map_continue(|((), state)| state)
345    }
346
347    fn fold<T: Fold<P, F>>(self, fold: T) -> T::Output;
348
349    /// Retags an open route arm as finished while retaining the same route,
350    /// slot, payload owner, and generated-family discriminant storage.
351    fn finish(self) -> Result<Self, Self>
352    where
353        F: Finish;
354
355    fn each_open<'a, V: open::Each<F>>(&'a mut self, visit: V) -> V::Output<'a>
356    where
357        F: open::Set;
358
359    fn fold_open<V: open::Fold<P, F>>(self, visit: V) -> V::Output
360    where
361        F: open::Set;
362
363    fn consume<O, V: FnOnce(F::Body) -> O>(self, visit: V) -> O
364    where
365        F: open::Set;
366}
367
368/// Consuming unguarded transition into the application's admitted family.
369pub trait Admission<P, F: Set, G: Set>: Sized {
370    type Output;
371
372    fn admit<V: admit::Visit<P, F, G>>(self, visit: &mut V) -> ControlFlow<V::Break, Self::Output>;
373}