Skip to main content

behavior/effects/
actions.rs

1//! The explicit result of one actor behavior transition.
2
3use super::sending::{SendEffects, SendInput};
4use crate::actor::{Address, BirthMode, Create};
5use crate::next::{Never, Step, Stopped};
6
7pub type Become<Ph = Never> = Step<Ph, Stopped>;
8
9/// Capability to append one communication at a statically selected lane while
10/// preserving every other actor-transition effect.
11pub trait AppendSend<Input, Path>: Sized {
12    /// Append `input` exactly once and preserve creations and the next verdict.
13    #[must_use]
14    fn append_send(self, input: Input) -> Self;
15}
16
17/// Bombay's typed realization of the actor transition effects: communications,
18/// fresh actor creation, and next behavior or termination.
19///
20/// An interpreter resolves every fresh creation in `creates` before
21/// interpreting any ordinary delivery or [`crate::InterpreterRequests`] request in
22/// `sends` from this value. A successful resolution installs and binds the
23/// child; a rejected resolution binds nothing. This ordering lets a same-action
24/// typed creation-observation request return the committed result rather than
25/// the behavior's intent. When creation is rejected, a same-action
26/// child-observation request for its nonce is consumed without installing an
27/// observation or emitting a child-stopped fact, while the creation-observation
28/// request reports the rejection. A later creation cannot
29/// inherit that consumed observation. Creation order is vector order, and each
30/// concrete named send lane retains its own order; this contract does not
31/// impose an order between independent lanes. Constructing a value remains
32/// pure.
33pub struct Actions<A: Address, Ph, Sends, Birth: BirthMode> {
34    pub sends: Sends,
35    pub creates: Vec<Create<A, Birth::Child>>,
36    pub become_: Become<Ph>,
37}
38
39impl<A, Ph, Sends, Birth> core::fmt::Debug for Actions<A, Ph, Sends, Birth>
40where
41    A: Address + core::fmt::Debug,
42    A::Nonce: core::fmt::Debug,
43    Ph: core::fmt::Debug,
44    Sends: core::fmt::Debug,
45    Birth: BirthMode,
46    Birth::Child: core::fmt::Debug,
47{
48    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
49        formatter
50            .debug_struct("Actions")
51            .field("sends", &self.sends)
52            .field("creates", &self.creates)
53            .field("become", &self.become_)
54            .finish()
55    }
56}
57
58impl<A, Ph, Sends, Birth> PartialEq for Actions<A, Ph, Sends, Birth>
59where
60    A: Address + PartialEq,
61    A::Nonce: PartialEq,
62    Ph: PartialEq,
63    Sends: PartialEq,
64    Birth: BirthMode,
65    Birth::Child: PartialEq,
66{
67    fn eq(&self, other: &Self) -> bool {
68        self.sends == other.sends && self.creates == other.creates && self.become_ == other.become_
69    }
70}
71
72impl<A, Ph, Sends, Birth> Eq for Actions<A, Ph, Sends, Birth>
73where
74    A: Address + Eq,
75    A::Nonce: Eq,
76    Ph: Eq,
77    Sends: Eq,
78    Birth: BirthMode,
79    Birth::Child: Eq,
80{
81}
82
83impl<A: Address, Ph, Sends, Birth: BirthMode> Actions<A, Ph, Sends, Birth> {
84    /// Transform only the send effects, preserving creation order and the
85    /// next-behavior verdict exactly.
86    #[must_use]
87    pub fn map_sends<Mapped>(
88        self,
89        map: impl FnOnce(Sends) -> Mapped,
90    ) -> Actions<A, Ph, Mapped, Birth> {
91        Actions {
92            sends: map(self.sends),
93            creates: self.creates,
94            become_: self.become_,
95        }
96    }
97
98    /// Transform only the next-behavior verdict, preserving sends and
99    /// creation order exactly.
100    #[must_use]
101    pub fn map_become<NextPh>(
102        self,
103        map: impl FnOnce(Become<Ph>) -> Become<NextPh>,
104    ) -> Actions<A, NextPh, Sends, Birth> {
105        Actions {
106            sends: self.sends,
107            creates: self.creates,
108            become_: map(self.become_),
109        }
110    }
111
112    /// Append one communication to its statically selected send lane.
113    ///
114    /// This is a pure transformation of the send leg. It preserves creation
115    /// order and the exact continue, phase-change, or termination verdict.
116    /// `Path` is compile-time lane evidence; no runtime lookup is performed.
117    #[must_use]
118    pub fn with_send<Input, Path>(mut self, input: Input) -> Self
119    where
120        Sends: SendInput<Input, Path>,
121    {
122        <Sends as SendInput<Input, Path>>::emit(&mut self.sends, input);
123        self
124    }
125}
126
127impl<A, Ph, Sends, Birth, Input, Path> AppendSend<Input, Path> for Actions<A, Ph, Sends, Birth>
128where
129    A: Address,
130    Birth: BirthMode,
131    Sends: SendInput<Input, Path>,
132{
133    fn append_send(self, input: Input) -> Self {
134        self.with_send::<Input, Path>(input)
135    }
136}
137
138impl<A: Address, Ph, Sends: SendEffects, Birth: BirthMode> Actions<A, Ph, Sends, Birth> {
139    #[must_use]
140    pub const fn new(
141        sends: Sends,
142        creates: Vec<Create<A, Birth::Child>>,
143        become_: Become<Ph>,
144    ) -> Self {
145        Self {
146            sends,
147            creates,
148            become_,
149        }
150    }
151
152    #[must_use]
153    pub fn just(become_: Become<Ph>) -> Self {
154        Self {
155            sends: Sends::empty(),
156            creates: Vec::new(),
157            become_,
158        }
159    }
160
161    #[must_use]
162    pub fn cont() -> Self {
163        Self::just(Step::Continue)
164    }
165    #[must_use]
166    pub fn stop() -> Self {
167        Self::just(Step::Stop(Stopped))
168    }
169    #[must_use]
170    pub fn goto(phase: Ph) -> Self {
171        Self::just(Step::Goto(phase))
172    }
173
174    /// Continue after emitting the complete declared send product.
175    #[must_use]
176    pub fn send(sends: Sends) -> Self {
177        Self::new(sends, Vec::new(), Step::Continue)
178    }
179
180    /// Continue after staging the complete declared creation product.
181    #[must_use]
182    pub fn create(creates: Vec<Create<A, Birth::Child>>) -> Self {
183        Self::new(Sends::empty(), creates, Step::Continue)
184    }
185}
186
187impl<A: Address, Ph, Sends, Birth: BirthMode>
188    From<(Sends, Vec<Create<A, Birth::Child>>, Become<Ph>)> for Actions<A, Ph, Sends, Birth>
189{
190    fn from((sends, creates, become_): (Sends, Vec<Create<A, Birth::Child>>, Become<Ph>)) -> Self {
191        Self {
192            sends,
193            creates,
194            become_,
195        }
196    }
197}
198
199pub type Acted<A, Ph, Sends, Birth, E> = Result<Actions<A, Ph, Sends, Birth>, E>;
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204    use crate::{Births, CreationKind, MailAddr, NoBirths, Own};
205
206    #[test]
207    fn equality_and_debug_cover_every_named_effect_leg() {
208        type Plain = Actions<MailAddr, u8, Vec<u8>, NoBirths>;
209        type Creating = Actions<MailAddr, Never, Vec<u8>, Births<u8>>;
210
211        let value = Plain::new(vec![1], Vec::new(), Step::Goto(3));
212        assert_eq!(value, Plain::new(vec![1], Vec::new(), Step::Goto(3)));
213        assert_ne!(value, Plain::new(vec![2], Vec::new(), Step::Goto(3)));
214        assert_ne!(value, Plain::new(vec![1], Vec::new(), Step::Goto(4)));
215        assert_ne!(value, Plain::new(vec![1], Vec::new(), Step::Continue));
216        assert_eq!(
217            format!("{value:?}"),
218            "Actions { sends: [1], creates: [], become: Goto(3) }"
219        );
220
221        let created = Creating::new(
222            Vec::new(),
223            vec![Create::new(7, 9, CreationKind::Birth)],
224            Step::Continue,
225        );
226        let other_creation = Creating::new(
227            Vec::new(),
228            vec![Create::new(8, 9, CreationKind::Birth)],
229            Step::Continue,
230        );
231        assert_ne!(created, other_creation);
232    }
233
234    #[test]
235    fn mapping_sends_preserves_creation_order_and_verdict() {
236        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
237            vec![1, 2],
238            vec![
239                Create::new(3, (), CreationKind::Birth),
240                Create::new(4, (), CreationKind::replacement_of(3)),
241            ],
242            Step::Goto(7),
243        );
244
245        let mapped = actions.map_sends(|sends| sends.len());
246        assert_eq!(mapped.sends, 2);
247        assert_eq!(
248            mapped
249                .creates
250                .iter()
251                .map(|creation| creation.nonce)
252                .collect::<Vec<_>>(),
253            [3, 4]
254        );
255        assert!(matches!(mapped.become_, Step::Goto(7)));
256    }
257
258    #[test]
259    fn mapping_become_preserves_sends_and_creation_order() {
260        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
261            vec![1, 2],
262            vec![Create::new(3, (), CreationKind::Birth)],
263            Step::Goto(7),
264        );
265
266        let mapped: Actions<MailAddr, Never, Vec<u8>, Births<()>> =
267            actions.map_become(|_| Step::Stop(Stopped));
268        assert_eq!(mapped.sends, [1, 2]);
269        assert_eq!(mapped.creates[0].nonce, 3);
270        assert!(matches!(mapped.become_, Step::Stop(Stopped)));
271    }
272
273    #[test]
274    fn fluent_send_changes_only_the_selected_effect_leg() {
275        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
276            vec![1],
277            vec![
278                Create::new(3, (), CreationKind::Birth),
279                Create::new(4, (), CreationKind::replacement_of(3)),
280            ],
281            Step::Goto(7),
282        )
283        .with_send::<_, Own>(2)
284        .with_send::<_, Own>(3);
285
286        assert_eq!(actions.sends, [1, 2, 3]);
287        assert_eq!(
288            actions
289                .creates
290                .iter()
291                .map(|creation| creation.nonce)
292                .collect::<Vec<_>>(),
293            [3, 4]
294        );
295        assert!(matches!(actions.become_, Step::Goto(7)));
296
297        let stopped: Actions<MailAddr, Never, Vec<u8>, NoBirths> =
298            Actions::stop().with_send::<_, Own>(5);
299        assert_eq!(stopped.sends, [5]);
300        assert!(matches!(stopped.become_, Step::Stop(Stopped)));
301    }
302}