Skip to main content

behavior/effects/
sending.rs

1//! Typed send products and their accumulation contract.
2
3use core::marker::PhantomData;
4
5/// The lane owned by the current named send product.
6pub enum Own {}
7
8/// A lane reached through the product's composed behavior sends.
9pub struct Inner<Path>(PhantomData<fn(Path)>);
10
11/// Static evidence that a send algebra contains one request lane.
12///
13/// Implementations append the input exactly once to that lane and leave every
14/// other lane unchanged. `Path` distinguishes repeated request types without
15/// erasing their position or choosing a lane at runtime.
16///
17/// [`Own`] selects the current algebra's owned lane. [`Inner<Path>`] descends
18/// through the composed-behavior side of one product or named wrapper before
19/// applying `Path`. Consequently, paths remain statically known through
20/// arbitrary wrapper depth.
21///
22/// ```compile_fail
23/// use behavior::{Inner, Own, SendAlgebra, SendProduct};
24///
25/// let mut sends = SendProduct::new(Vec::<u8>::new(), Vec::<u16>::new());
26/// // `u32` is not accepted by either lane at this path.
27/// sends.send::<_, Inner<Own>>(1_u32);
28/// ```
29pub trait SendInput<Input, Path> {
30    fn emit(&mut self, input: Input);
31}
32
33/// A product of independently typed send protocols.
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct SendProduct<L, R> {
36    pub inner: L,
37    pub own: R,
38}
39
40impl<L, R> SendProduct<L, R> {
41    #[must_use]
42    pub const fn new(inner: L, own: R) -> Self {
43        Self { inner, own }
44    }
45
46    #[must_use]
47    pub fn split(self) -> (L, R) {
48        (self.inner, self.own)
49    }
50}
51
52impl<L, R> From<(L, R)> for SendProduct<L, R> {
53    fn from((inner, own): (L, R)) -> Self {
54        Self::new(inner, own)
55    }
56}
57
58/// The operation required to accumulate sends across transitions.
59pub trait SendAlgebra: Sized {
60    fn empty() -> Self;
61    fn append(&mut self, other: Self);
62
63    #[must_use]
64    fn combine(mut self, other: Self) -> Self {
65        self.append(other);
66        self
67    }
68
69    /// Append one request to its statically selected semantic lane.
70    fn send<Input, Path>(&mut self, input: Input)
71    where
72        Self: SendInput<Input, Path>,
73    {
74        <Self as SendInput<Input, Path>>::emit(self, input);
75    }
76
77    /// Build a send product containing one request in its selected lane.
78    #[must_use]
79    fn sending<Input, Path>(input: Input) -> Self
80    where
81        Self: SendInput<Input, Path>,
82    {
83        let mut sends = Self::empty();
84        sends.send(input);
85        sends
86    }
87}
88
89impl<T> SendAlgebra for Vec<T> {
90    fn empty() -> Self {
91        Vec::new()
92    }
93
94    fn append(&mut self, mut other: Self) {
95        Vec::append(self, &mut other);
96    }
97}
98
99impl<T> SendInput<T, Own> for Vec<T> {
100    fn emit(&mut self, input: T) {
101        self.push(input);
102    }
103}
104
105impl<L, R, Input> SendInput<Input, Own> for SendProduct<L, R>
106where
107    R: SendInput<Input, Own>,
108{
109    fn emit(&mut self, input: Input) {
110        <R as SendInput<Input, Own>>::emit(&mut self.own, input);
111    }
112}
113
114impl<L, R, Input, Path> SendInput<Input, Inner<Path>> for SendProduct<L, R>
115where
116    L: SendInput<Input, Path>,
117{
118    fn emit(&mut self, input: Input) {
119        <L as SendInput<Input, Path>>::emit(&mut self.inner, input);
120    }
121}
122
123impl<L: SendAlgebra, R: SendAlgebra> SendAlgebra for SendProduct<L, R> {
124    fn empty() -> Self {
125        Self::new(L::empty(), R::empty())
126    }
127
128    fn append(&mut self, other: Self) {
129        let (inner, own) = other.split();
130        self.inner.append(inner);
131        self.own.append(own);
132    }
133}
134
135/// Requests interpreted by the runtime local to the emitting actor.
136///
137/// Unlike [`crate::Delivery`], a service request has no actor address. Its
138/// recipient is definitionally the interpreter of the actor whose transition
139/// emitted it. This distinct send lane lets interpreters route ordinary
140/// deliveries and local services with disjoint static implementations.
141#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct ServiceSends<M> {
143    requests: Vec<M>,
144}
145
146impl<M> ServiceSends<M> {
147    #[must_use]
148    pub fn new(requests: Vec<M>) -> Self {
149        Self { requests }
150    }
151    #[must_use]
152    pub fn one(request: M) -> Self {
153        Self::new(vec![request])
154    }
155    #[must_use]
156    pub fn as_slice(&self) -> &[M] {
157        &self.requests
158    }
159    pub fn iter(&self) -> core::slice::Iter<'_, M> {
160        self.requests.iter()
161    }
162    #[must_use]
163    pub fn len(&self) -> usize {
164        self.requests.len()
165    }
166    #[must_use]
167    pub fn is_empty(&self) -> bool {
168        self.requests.is_empty()
169    }
170    pub fn extend(&mut self, requests: impl IntoIterator<Item = M>) {
171        self.requests.extend(requests);
172    }
173    #[must_use]
174    pub fn into_requests(self) -> Vec<M> {
175        self.requests
176    }
177}
178
179impl<M> core::ops::Index<usize> for ServiceSends<M> {
180    type Output = M;
181    fn index(&self, index: usize) -> &Self::Output {
182        &self.requests[index]
183    }
184}
185
186impl<M> IntoIterator for ServiceSends<M> {
187    type Item = M;
188    type IntoIter = std::vec::IntoIter<M>;
189    fn into_iter(self) -> Self::IntoIter {
190        self.requests.into_iter()
191    }
192}
193
194impl<'a, M> IntoIterator for &'a ServiceSends<M> {
195    type Item = &'a M;
196    type IntoIter = core::slice::Iter<'a, M>;
197    fn into_iter(self) -> Self::IntoIter {
198        self.requests.iter()
199    }
200}
201
202impl<M> SendAlgebra for ServiceSends<M> {
203    fn empty() -> Self {
204        Self::new(Vec::new())
205    }
206    fn append(&mut self, mut other: Self) {
207        self.requests.append(&mut other.requests);
208    }
209}
210
211impl<M> SendInput<M, Own> for ServiceSends<M> {
212    fn emit(&mut self, input: M) {
213        self.requests.push(input);
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220    use proptest::prelude::*;
221
222    #[test]
223    fn send_algebra_obeys_identity_and_associativity() {
224        let values = vec![1, 2];
225        assert_eq!(Vec::new().combine(values.clone()), values);
226        assert_eq!(values.clone().combine(Vec::new()), values);
227
228        let left = vec![1].combine(vec![2]).combine(vec![3]);
229        let right = vec![1].combine(vec![2].combine(vec![3]));
230        assert_eq!(left, right);
231    }
232
233    #[test]
234    fn typed_paths_select_exactly_one_product_lane() {
235        type Sends = SendProduct<SendProduct<Vec<u8>, Vec<u16>>, Vec<u32>>;
236
237        let mut sends = Sends::empty();
238        sends.send::<_, Inner<Inner<Own>>>(1_u8);
239        sends.send::<_, Inner<Own>>(2_u16);
240        sends.send::<_, Own>(3_u32);
241
242        assert_eq!(sends.inner.inner, vec![1]);
243        assert_eq!(sends.inner.own, vec![2]);
244        assert_eq!(sends.own, vec![3]);
245    }
246
247    proptest! {
248        #[test]
249        fn typed_path_emission_preserves_every_unselected_lane(
250            inner in proptest::collection::vec(any::<u8>(), 0..16),
251            middle in proptest::collection::vec(any::<u16>(), 0..16),
252            own in proptest::collection::vec(any::<u32>(), 0..16),
253            input in any::<u16>(),
254        ) {
255            let original_inner = inner.clone();
256            let original_own = own.clone();
257            let mut sends = SendProduct::new(SendProduct::new(inner, middle.clone()), own);
258
259            sends.send::<_, Inner<Own>>(input);
260
261            prop_assert_eq!(sends.inner.inner, original_inner);
262            prop_assert_eq!(sends.inner.own.len(), middle.len() + 1);
263            prop_assert_eq!(&sends.inner.own[..middle.len()], middle.as_slice());
264            prop_assert_eq!(sends.inner.own.last(), Some(&input));
265            prop_assert_eq!(sends.own, original_own);
266        }
267    }
268}