1use crate::actor::Address;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct Here;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct Inside<Path>(core::marker::PhantomData<fn() -> Path>);
12
13pub 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 #[must_use]
35 pub const fn new() -> Self {
36 Self {
37 marker: core::marker::PhantomData,
38 }
39 }
40
41 #[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 #[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
79pub trait EventIngress<Source, Input>: Sized {
92 fn ingress(input: Input) -> Self;
94}
95
96pub trait ChildInputIngress<Source, Input>: Sized {
109 fn child_input(input: Input) -> Self;
111
112 fn recover_child_input(event: Self) -> Result<Input, Self>;
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
128pub enum EventLayer<Owned, Inner> {
129 Owned(Owned),
130 Inner(Inner),
131}
132
133impl<Owned, Inner, Source, Input> ChildInputIngress<Source, Input> for EventLayer<Owned, Inner>
134where
135 Inner: ChildInputIngress<Source, Input>,
136{
137 fn child_input(input: Input) -> Self {
138 Self::Inner(Inner::child_input(input))
139 }
140
141 fn recover_child_input(event: Self) -> Result<Input, Self> {
142 match event {
143 Self::Inner(inner) => Inner::recover_child_input(inner).map_err(Self::Inner),
144 owned @ Self::Owned(_) => Err(owned),
145 }
146 }
147}
148
149impl<Owned, Inner, Source, Input> EventIngress<Source, Input> for EventLayer<Owned, Inner>
150where
151 Inner: EventIngress<Source, Input>,
152{
153 fn ingress(input: Input) -> Self {
154 Self::Inner(Inner::ingress(input))
155 }
156}
157
158pub trait ComposedEvent: UserEvent {
165 type Inner: UserEvent<Addr = Self::Addr, Message = Self::Message>;
166
167 fn from_inner(event: Self::Inner) -> Self;
168}
169
170impl<Owned, Inner> ComposedEvent for EventLayer<Owned, Inner>
171where
172 Inner: UserEvent,
173{
174 type Inner = Inner;
175
176 fn from_inner(event: Inner) -> Self {
177 Self::Inner(event)
178 }
179}
180
181pub trait InjectEvent<Input, Path>: Sized {
200 fn inject_at(input: Input) -> Self;
201}
202
203pub trait RecoverEvent<Input, Path>: Sized {
221 fn recover(event: Self) -> Result<Input, Self>;
226}
227
228impl<Owned, Inner> InjectEvent<Owned, Here> for EventLayer<Owned, Inner> {
229 fn inject_at(input: Owned) -> Self {
230 Self::Owned(input)
231 }
232}
233
234impl<Owned, Inner> RecoverEvent<Owned, Here> for EventLayer<Owned, Inner> {
235 fn recover(event: Self) -> Result<Owned, Self> {
236 match event {
237 Self::Owned(input) => Ok(input),
238 other @ Self::Inner(_) => Err(other),
239 }
240 }
241}
242
243impl<Owned, Inner, Input, Path> InjectEvent<Input, Inside<Path>> for EventLayer<Owned, Inner>
244where
245 Inner: InjectEvent<Input, Path>,
246{
247 fn inject_at(input: Input) -> Self {
248 Self::Inner(Inner::inject_at(input))
249 }
250}
251
252impl<Owned, Inner, Input, Path> RecoverEvent<Input, Inside<Path>> for EventLayer<Owned, Inner>
253where
254 Inner: RecoverEvent<Input, Path>,
255{
256 fn recover(event: Self) -> Result<Input, Self> {
257 match event {
258 Self::Inner(inner) => Inner::recover(inner).map_err(Self::Inner),
259 other @ Self::Owned(_) => Err(other),
260 }
261 }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq)]
266pub struct User<A, M> {
267 pub from: A,
268 pub message: M,
269}
270
271impl<A, M> User<A, M> {
272 #[must_use]
273 pub const fn new(from: A, message: M) -> Self {
274 Self { from, message }
275 }
276}
277
278impl<A, M> From<(A, M)> for User<A, M> {
279 fn from((from, message): (A, M)) -> Self {
280 Self::new(from, message)
281 }
282}
283
284pub trait UserEvent: Sized {
286 type Addr: Address;
287 type Message;
288
289 fn user(from: Self::Addr, message: Self::Message) -> Self;
290
291 fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self>;
294}
295
296impl<A: Address, M> UserEvent for User<A, M> {
297 type Addr = A;
298 type Message = M;
299
300 fn user(from: A, message: M) -> Self {
301 Self::new(from, message)
302 }
303 fn into_user(self) -> Result<Self, Self> {
304 Ok(self)
305 }
306}
307
308impl<Owned, Inner> UserEvent for EventLayer<Owned, Inner>
309where
310 Inner: UserEvent,
311{
312 type Addr = Inner::Addr;
313 type Message = Inner::Message;
314
315 fn user(from: Self::Addr, message: Self::Message) -> Self {
316 Self::Inner(Inner::user(from, message))
317 }
318
319 fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self> {
320 match self {
321 Self::Inner(inner) => inner.into_user().map_err(Self::Inner),
322 owned @ Self::Owned(_) => Err(owned),
323 }
324 }
325}
326
327#[cfg(test)]
328mod structural_tests {
329 use super::*;
330 use crate::MailAddr;
331
332 type Nested = EventLayer<u8, EventLayer<u16, User<MailAddr, ()>>>;
333
334 #[test]
335 fn paths_select_exactly_one_layer_without_runtime_search() {
336 let outer = <Nested as InjectEvent<u8, Here>>::inject_at(3);
337 assert_eq!(outer, EventLayer::Owned(3));
338
339 let inner = <Nested as InjectEvent<u16, Inside<Here>>>::inject_at(5);
340 assert_eq!(inner, EventLayer::Inner(EventLayer::Owned(5)));
341 }
342
343 #[test]
344 fn duplicate_payload_types_remain_distinct_ownership_capabilities() {
345 type Duplicate = EventLayer<u8, EventLayer<u8, User<MailAddr, ()>>>;
346
347 let outer = <Duplicate as InjectEvent<u8, Here>>::inject_at(7);
348 let inner = <Duplicate as InjectEvent<u8, Inside<Here>>>::inject_at(7);
349
350 assert_eq!(outer, EventLayer::Owned(7));
351 assert_eq!(inner, EventLayer::Inner(EventLayer::Owned(7)));
352 }
353
354 #[test]
355 fn a_unique_structural_path_is_inferred_at_a_concrete_composition() {
356 let event = <Nested as InjectEvent<u16, _>>::inject_at(9);
357 assert_eq!(event, EventLayer::Inner(EventLayer::Owned(9)));
358 }
359
360 #[test]
361 fn ingress_identity_lifts_without_payload_or_runtime_route_search() {
362 type Inner = EventLayer<u16, User<MailAddr, ()>>;
363 type Outer = EventLayer<u8, Inner>;
364
365 let inner = Ingress::<u16, Here>::new();
366 let outer: Ingress<u16, Inside<Here>> = inner.inside();
367
368 let event: Outer = outer.event(12);
369 assert_eq!(event, EventLayer::Inner(EventLayer::Owned(12)));
370 }
371
372 #[test]
373 #[allow(
374 clippy::clone_on_copy,
375 reason = "the contract test independently exercises both promised construction traits"
376 )]
377 fn ingress_is_a_copyable_equal_capability_with_stable_debug_identity() {
378 let ingress = Ingress::<u16, Here>::new();
379 let copied = ingress;
380 let cloned = ingress.clone();
381
382 assert_eq!(ingress, copied);
383 assert_eq!(ingress, cloned);
384 assert_eq!(format!("{ingress:?}"), "Ingress");
385 }
386}