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
79#[derive(Debug, Clone, PartialEq, Eq)]
85pub enum EventLayer<Owned, Inner> {
86 Owned(Owned),
87 Inner(Inner),
88}
89
90pub 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
113pub 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#[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
172pub trait UserEvent: Sized {
174 type Addr: Address;
175 type Message;
176
177 fn user(from: Self::Addr, message: Self::Message) -> Self;
178
179 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}