Skip to main content

behavior/
stash.rs

1//! Pure message holding and replay composition.
2
3use std::collections::VecDeque;
4
5use crate::behavior::{Actions, Address, Behavior, BirthMode, SendAlgebra, User, UserEvent};
6use crate::next::{Never, Step};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum StashRoute {
10    Stash,
11    Deliver,
12    Release,
13}
14
15/// Semantic observation of the outermost composed stash, independent of its
16/// structural nesting depth.
17pub trait StashStatus {
18    fn stashed_messages(&self) -> usize;
19}
20
21pub struct Stash<B: Behavior> {
22    inner: B,
23    route: fn(&B::Msg) -> StashRoute,
24    held: VecDeque<User<B::Addr, B::Msg>>,
25}
26
27impl<B: Behavior<Ph = Never>> Stash<B> {
28    #[must_use]
29    pub(crate) fn new(inner: B, route: fn(&B::Msg) -> StashRoute) -> Self {
30        Self {
31            inner,
32            route,
33            held: VecDeque::new(),
34        }
35    }
36
37    #[must_use]
38    pub fn held(&self) -> usize {
39        self.held.len()
40    }
41}
42
43impl<B: Behavior<Ph = Never>> StashStatus for Stash<B> {
44    fn stashed_messages(&self) -> usize {
45        self.held()
46    }
47}
48
49impl<B> crate::BehaviorBase for Stash<B>
50where
51    B: Behavior<Ph = Never> + crate::BehaviorBase,
52{
53    type Base = B::Base;
54
55    fn base(&self) -> &Self::Base {
56        self.inner.base()
57    }
58}
59
60impl<B, A, Sends, Br> Stash<B>
61where
62    A: Address,
63    Sends: SendAlgebra,
64    Br: BirthMode,
65    B: Behavior<Addr = A, Ph = Never, Sends = Sends, Birth = Br>,
66{
67    fn drain_into(
68        &mut self,
69        acc: &mut Actions<B::Addr, Never, B::Sends, B::Birth>,
70    ) -> Result<(), B::Error> {
71        let mut batch: VecDeque<_> = self.held.drain(..).collect();
72        while let Some(user) = batch.pop_front() {
73            match (self.route)(&user.message) {
74                StashRoute::Stash => self.held.push_back(user),
75                StashRoute::Deliver | StashRoute::Release => {
76                    let actions = crate::calculus::delegate_transition(
77                        &mut self.inner,
78                        B::Event::user(user.from, user.message),
79                    )?;
80                    acc.sends.append(actions.sends);
81                    acc.creates.extend(actions.creates);
82                    if let Step::Stop(exit) = actions.become_ {
83                        self.held.extend(batch);
84                        acc.become_ = Step::Stop(exit);
85                        return Ok(());
86                    }
87                }
88            }
89        }
90        Ok(())
91    }
92}
93
94impl<B, A, Sends, Br> Behavior for Stash<B>
95where
96    A: Address,
97    Sends: SendAlgebra,
98    Br: BirthMode,
99    B: Behavior<Addr = A, Ph = Never, Sends = Sends, Birth = Br>,
100{
101    type Addr = A;
102    type Msg = B::Msg;
103    type Event = B::Event;
104    type Sends = Sends;
105    type Ph = Never;
106    type Error = B::Error;
107    type Birth = Br;
108
109    fn init(
110        &mut self,
111        _: crate::InitializationTurn,
112    ) -> Result<Actions<A, Never, Sends, Br>, B::Error> {
113        crate::calculus::initialize(&mut self.inner)
114    }
115
116    fn transition(
117        &mut self,
118        _: crate::ActiveTurn,
119        event: B::Event,
120    ) -> Result<Actions<A, Never, Sends, Br>, B::Error> {
121        let user = match event.into_user() {
122            Ok(user) => user,
123            Err(other) => return crate::calculus::delegate_transition(&mut self.inner, other),
124        };
125        match (self.route)(&user.message) {
126            StashRoute::Stash => {
127                self.held.push_back(user);
128                Ok(Actions::cont())
129            }
130            StashRoute::Deliver => crate::calculus::delegate_transition(
131                &mut self.inner,
132                B::Event::user(user.from, user.message),
133            ),
134            StashRoute::Release => {
135                let mut actions = crate::calculus::delegate_transition(
136                    &mut self.inner,
137                    B::Event::user(user.from, user.message),
138                )?;
139                if !matches!(actions.become_, Step::Stop(_)) {
140                    self.drain_into(&mut actions)?;
141                }
142                Ok(actions)
143            }
144        }
145    }
146}