Skip to main content

behavior/
user_event.rs

1//! The user-message lane and its composition contracts.
2
3use crate::actor::Address;
4
5/// The current layer of a structurally composed event algebra owns an input.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct Here;
8
9/// An inner event algebra owns an input at `Path`.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct Inside<Path>(core::marker::PhantomData<fn() -> Path>);
12
13/// Address-free destination for one interpreter-originated input.
14///
15/// Unlike an actor [`Recipient`](crate::Recipient), this capability targets one
16/// exact member of the current actor's composed ingress algebra. Service
17/// requests retain it so the eventual fact does not have to search the final
18/// behavior type for a matching payload. `Path` is compile-time evidence and
19/// occupies no runtime storage.
20pub struct Ingress<Input, Path> {
21    marker: core::marker::PhantomData<fn(Input, Path)>,
22}
23
24impl<Input, Path> Copy for Ingress<Input, Path> {}
25
26impl<Input, Path> Clone for Ingress<Input, Path> {
27    fn clone(&self) -> Self {
28        *self
29    }
30}
31
32impl<Input, Path> Ingress<Input, Path> {
33    /// Select a statically proven ingress member.
34    #[must_use]
35    pub const fn new() -> Self {
36        Self {
37            marker: core::marker::PhantomData,
38        }
39    }
40
41    /// Construct the exact concrete event without runtime route discovery.
42    #[must_use]
43    pub fn event<Event>(self, input: Input) -> Event
44    where
45        Event: InjectEvent<Input, Path>,
46    {
47        Event::inject_at(input)
48    }
49
50    /// Lift this destination through one outer structural event layer.
51    #[must_use]
52    pub const fn inside(self) -> Ingress<Input, Inside<Path>> {
53        Ingress {
54            marker: core::marker::PhantomData,
55        }
56    }
57}
58
59impl<Input, Path> Default for Ingress<Input, Path> {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl<Input, Path> core::fmt::Debug for Ingress<Input, Path> {
66    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67        formatter.write_str("Ingress")
68    }
69}
70
71impl<Input, Path> PartialEq for Ingress<Input, Path> {
72    fn eq(&self, _: &Self) -> bool {
73        true
74    }
75}
76
77impl<Input, Path> Eq for Ingress<Input, Path> {}
78
79/// One statically owned input lane composed in front of an inner event algebra.
80///
81/// `EventLayer` is a concrete coproduct, not an erased envelope. `Owned` is the
82/// lane introduced by the current behavior template and `Inner` preserves the
83/// complete event algebra of the wrapped behavior.
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub enum EventLayer<Owned, Inner> {
86    Owned(Owned),
87    Inner(Inner),
88}
89
90/// A complete event algebra formed by adding owned inputs around an inner
91/// behavior event algebra.
92///
93/// `from_inner` is the structure-preserving injection used by effect
94/// composition. A single owned lane uses [`EventLayer`]; a domain with several
95/// genuinely coexisting owned inputs may use a named exhaustive sum.
96pub trait ComposedEvent: UserEvent {
97    type Inner: UserEvent<Addr = Self::Addr, Message = Self::Message>;
98
99    fn from_inner(event: Self::Inner) -> Self;
100}
101
102impl<Owned, Inner> ComposedEvent for EventLayer<Owned, Inner>
103where
104    Inner: UserEvent,
105{
106    type Inner = Inner;
107
108    fn from_inner(event: Inner) -> Self {
109        Self::Inner(event)
110    }
111}
112
113/// Path-indexed injection into a structural event coproduct.
114///
115/// The path is compile-time routing evidence. It prevents the overlapping
116/// implementations that arise when an outer layer owns the same input type as
117/// an inner layer and makes that ownership choice explicit in the type system.
118/// Unsupported input has no construction capability:
119///
120/// ```compile_fail
121/// use behavior::{Here, InjectEvent, MailAddr, User};
122/// let _ = <User<MailAddr, ()> as InjectEvent<u8, Here>>::inject_at(7);
123/// ```
124///
125/// Repeated payload types require an explicit ownership path rather than an
126/// outermost-first runtime guess:
127///
128/// ```compile_fail
129/// use behavior::{EventLayer, InjectEvent, MailAddr, User};
130/// type Duplicate = EventLayer<u8, EventLayer<u8, User<MailAddr, ()>>>;
131/// let _ = <Duplicate as InjectEvent<u8, _>>::inject_at(7);
132/// ```
133pub trait InjectEvent<Input, Path>: Sized {
134    fn inject_at(input: Input) -> Self;
135}
136
137impl<Owned, Inner> InjectEvent<Owned, Here> for EventLayer<Owned, Inner> {
138    fn inject_at(input: Owned) -> Self {
139        Self::Owned(input)
140    }
141}
142
143impl<Owned, Inner, Input, Path> InjectEvent<Input, Inside<Path>> for EventLayer<Owned, Inner>
144where
145    Inner: InjectEvent<Input, Path>,
146{
147    fn inject_at(input: Input) -> Self {
148        Self::Inner(Inner::inject_at(input))
149    }
150}
151
152/// The user-message event at the Agha floor.
153#[derive(Debug, Clone, PartialEq, Eq)]
154pub struct User<A, M> {
155    pub from: A,
156    pub message: M,
157}
158
159impl<A, M> User<A, M> {
160    #[must_use]
161    pub const fn new(from: A, message: M) -> Self {
162        Self { from, message }
163    }
164}
165
166impl<A, M> From<(A, M)> for User<A, M> {
167    fn from((from, message): (A, M)) -> Self {
168        Self::new(from, message)
169    }
170}
171
172/// Construction/extraction of the user lane through a composed event type.
173pub trait UserEvent: Sized {
174    type Addr: Address;
175    type Message;
176
177    fn user(from: Self::Addr, message: Self::Message) -> Self;
178
179    /// # Errors
180    /// Returns the unchanged event when it belongs to another composed lane.
181    fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self>;
182}
183
184impl<A: Address, M> UserEvent for User<A, M> {
185    type Addr = A;
186    type Message = M;
187
188    fn user(from: A, message: M) -> Self {
189        Self::new(from, message)
190    }
191    fn into_user(self) -> Result<Self, Self> {
192        Ok(self)
193    }
194}
195
196impl<Owned, Inner> UserEvent for EventLayer<Owned, Inner>
197where
198    Inner: UserEvent,
199{
200    type Addr = Inner::Addr;
201    type Message = Inner::Message;
202
203    fn user(from: Self::Addr, message: Self::Message) -> Self {
204        Self::Inner(Inner::user(from, message))
205    }
206
207    fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self> {
208        match self {
209            Self::Inner(inner) => inner.into_user().map_err(Self::Inner),
210            owned @ Self::Owned(_) => Err(owned),
211        }
212    }
213}
214
215#[cfg(test)]
216mod structural_tests {
217    use super::*;
218    use crate::MailAddr;
219
220    type Nested = EventLayer<u8, EventLayer<u16, User<MailAddr, ()>>>;
221
222    #[test]
223    fn paths_select_exactly_one_layer_without_runtime_search() {
224        let outer = <Nested as InjectEvent<u8, Here>>::inject_at(3);
225        assert_eq!(outer, EventLayer::Owned(3));
226
227        let inner = <Nested as InjectEvent<u16, Inside<Here>>>::inject_at(5);
228        assert_eq!(inner, EventLayer::Inner(EventLayer::Owned(5)));
229    }
230
231    #[test]
232    fn duplicate_payload_types_remain_distinct_ownership_capabilities() {
233        type Duplicate = EventLayer<u8, EventLayer<u8, User<MailAddr, ()>>>;
234
235        let outer = <Duplicate as InjectEvent<u8, Here>>::inject_at(7);
236        let inner = <Duplicate as InjectEvent<u8, Inside<Here>>>::inject_at(7);
237
238        assert_eq!(outer, EventLayer::Owned(7));
239        assert_eq!(inner, EventLayer::Inner(EventLayer::Owned(7)));
240    }
241
242    #[test]
243    fn a_unique_structural_path_is_inferred_at_a_concrete_composition() {
244        let event = <Nested as InjectEvent<u16, _>>::inject_at(9);
245        assert_eq!(event, EventLayer::Inner(EventLayer::Owned(9)));
246    }
247
248    #[test]
249    fn ingress_identity_lifts_without_payload_or_runtime_route_search() {
250        type Inner = EventLayer<u16, User<MailAddr, ()>>;
251        type Outer = EventLayer<u8, Inner>;
252
253        let inner = Ingress::<u16, Here>::new();
254        let outer: Ingress<u16, Inside<Here>> = inner.inside();
255
256        let event: Outer = outer.event(12);
257        assert_eq!(event, EventLayer::Inner(EventLayer::Owned(12)));
258    }
259
260    #[test]
261    fn ingress_is_a_copyable_equal_capability_with_stable_debug_identity() {
262        let ingress = Ingress::<u16, Here>::new();
263        let copied = ingress;
264        let cloned = ingress.clone();
265
266        assert_eq!(ingress, copied);
267        assert_eq!(ingress, cloned);
268        assert_eq!(format!("{ingress:?}"), "Ingress");
269    }
270}