1use core::marker::PhantomData;
4
5use crate::{MessageProtocol, Protocol};
6
7#[cfg(test)]
8use crate::Behavior;
9
10pub trait Address: Copy + Eq {
12 type Nonce: Copy + Eq;
13
14 #[must_use]
15 fn birth(self, nonce: Self::Nonce) -> Self;
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct MailAddr(pub u64);
20
21impl From<u64> for MailAddr {
22 fn from(value: u64) -> Self {
23 Self(value)
24 }
25}
26
27impl From<MailAddr> for u64 {
28 fn from(value: MailAddr) -> Self {
29 value.0
30 }
31}
32
33impl Address for MailAddr {
34 type Nonce = u64;
35
36 fn birth(self, nonce: u64) -> Self {
37 Self(self.0 ^ nonce.wrapping_mul(0x9E37_79B9_7F4A_7C15))
38 }
39}
40
41pub struct Recipient<P: Protocol> {
64 address: P::Addr,
65 protocol: PhantomData<fn() -> P>,
66}
67
68impl<P: Protocol> Copy for Recipient<P> {}
69
70impl<P: Protocol> Clone for Recipient<P> {
71 fn clone(&self) -> Self {
72 *self
73 }
74}
75
76impl<P: Protocol> Recipient<P> {
77 #[must_use]
78 pub fn global(address: P::Addr) -> Self {
79 Self::new(address)
80 }
81
82 #[must_use]
84 pub const fn address(self) -> P::Addr {
85 self.address
86 }
87
88 #[doc(hidden)]
89 const fn new(address: P::Addr) -> Self {
90 Self {
91 address,
92 protocol: PhantomData,
93 }
94 }
95}
96
97impl<A: Address, M> From<A> for Recipient<MessageProtocol<A, M>> {
98 fn from(address: A) -> Self {
99 Self::new(address)
100 }
101}
102
103pub struct ChildRecipient<P: Protocol> {
109 nonce: <P::Addr as Address>::Nonce,
110 protocol: PhantomData<fn() -> P>,
111}
112
113impl<P: Protocol> Copy for ChildRecipient<P> {}
114impl<P: Protocol> Clone for ChildRecipient<P> {
115 fn clone(&self) -> Self {
116 *self
117 }
118}
119
120impl<P: Protocol> ChildRecipient<P> {
121 #[must_use]
122 pub const fn new(nonce: <P::Addr as Address>::Nonce) -> Self {
123 Self {
124 nonce,
125 protocol: PhantomData,
126 }
127 }
128
129 #[must_use]
130 pub const fn nonce(self) -> <P::Addr as Address>::Nonce {
131 self.nonce
132 }
133}
134
135pub enum DeliveryTarget<P: Protocol> {
137 Established(Recipient<P>),
138 LocalChild(ChildRecipient<P>),
139}
140
141impl<P: Protocol> Copy for DeliveryTarget<P> {}
142impl<P: Protocol> Clone for DeliveryTarget<P> {
143 fn clone(&self) -> Self {
144 *self
145 }
146}
147
148impl<P: Protocol> PartialEq for ChildRecipient<P> {
149 fn eq(&self, other: &Self) -> bool {
150 self.nonce == other.nonce
151 }
152}
153impl<P: Protocol> Eq for ChildRecipient<P> {}
154
155impl<P: Protocol> PartialEq for DeliveryTarget<P> {
156 fn eq(&self, other: &Self) -> bool {
157 match (self, other) {
158 (Self::Established(left), Self::Established(right)) => left == right,
159 (Self::LocalChild(left), Self::LocalChild(right)) => left == right,
160 _ => false,
161 }
162 }
163}
164impl<P: Protocol> Eq for DeliveryTarget<P> {}
165
166impl<P: Protocol> PartialEq<Recipient<P>> for DeliveryTarget<P> {
167 fn eq(&self, other: &Recipient<P>) -> bool {
168 matches!(self, Self::Established(recipient) if recipient == other)
169 }
170}
171
172impl<P: Protocol> core::fmt::Debug for DeliveryTarget<P>
173where
174 P::Addr: core::fmt::Debug,
175 <P::Addr as Address>::Nonce: core::fmt::Debug,
176{
177 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178 match self {
179 Self::Established(recipient) => f.debug_tuple("Established").field(recipient).finish(),
180 Self::LocalChild(child) => f.debug_tuple("LocalChild").field(&child.nonce()).finish(),
181 }
182 }
183}
184
185impl<P: Protocol> DeliveryTarget<P> {
186 #[must_use]
187 pub fn resolve(self, emitter: P::Addr) -> P::Addr {
188 match self {
189 Self::Established(recipient) => recipient.address(),
190 Self::LocalChild(child) => emitter.birth(child.nonce()),
191 }
192 }
193
194 #[doc(hidden)]
195 #[must_use]
196 pub fn is_local_child(self, expected: <P::Addr as Address>::Nonce) -> bool {
197 matches!(self, Self::LocalChild(child) if child.nonce() == expected)
198 }
199}
200
201impl<P: Protocol> PartialEq for Recipient<P> {
202 fn eq(&self, other: &Self) -> bool {
203 self.address == other.address
204 }
205}
206
207impl<P: Protocol> Eq for Recipient<P> {}
208
209impl<P: Protocol> core::fmt::Debug for Recipient<P>
210where
211 P::Addr: core::fmt::Debug,
212 <P::Addr as Address>::Nonce: core::fmt::Debug,
213{
214 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
215 self.address.fmt(f)
216 }
217}
218
219pub struct Delivery<P: Protocol> {
304 pub to: DeliveryTarget<P>,
305 pub message: P::Msg,
306}
307
308impl<P: Protocol> Delivery<P> {
309 #[must_use]
310 pub fn new(to: Recipient<P>, message: P::Msg) -> Self {
311 Self {
312 to: DeliveryTarget::Established(to),
313 message,
314 }
315 }
316
317 #[must_use]
318 pub fn local_child(to: ChildRecipient<P>, message: P::Msg) -> Self {
319 Self {
320 to: DeliveryTarget::LocalChild(to),
321 message,
322 }
323 }
324}
325
326impl<P> Clone for Delivery<P>
327where
328 P: Protocol,
329 P::Msg: Clone,
330{
331 fn clone(&self) -> Self {
332 Self {
333 to: self.to,
334 message: self.message.clone(),
335 }
336 }
337}
338
339impl<P> PartialEq for Delivery<P>
340where
341 P: Protocol,
342 P::Msg: PartialEq,
343{
344 fn eq(&self, other: &Self) -> bool {
345 self.to == other.to && self.message == other.message
346 }
347}
348
349impl<P> Eq for Delivery<P>
350where
351 P: Protocol,
352 P::Msg: Eq,
353{
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359 use crate::{Actions, Never, NoBirths, User};
360
361 struct Inbox;
362
363 struct SignatureOnly;
364
365 impl crate::Protocol for SignatureOnly {
366 type Addr = MailAddr;
367 type Msg = u8;
368 }
369
370 impl behavior::Protocol for Inbox {
371 type Addr = MailAddr;
372 type Msg = u8;
373 }
374
375 impl Behavior for Inbox {
376 type Protocol = Self;
377 type Event = User<MailAddr, u8>;
378 type Sends = Vec<Never>;
379 type Ph = Never;
380 type Error = Never;
381 type Birth = NoBirths;
382
383 fn init(&mut self, _: crate::InitializationTurn) -> crate::BehaviorActed<Self> {
384 Ok(Actions::cont())
385 }
386
387 fn transition(
388 &mut self,
389 _: crate::ActiveTurn,
390 _: Self::Event,
391 ) -> crate::BehaviorActed<Self> {
392 Ok(Actions::cont())
393 }
394 }
395
396 #[test]
397 fn mail_address_conversion_preserves_nonzero_value() {
398 assert_eq!(u64::from(MailAddr(41)), 41);
399 }
400
401 #[test]
402 fn routing_requires_only_the_static_protocol_signature() {
403 let recipient = Recipient::<SignatureOnly>::global(MailAddr(7));
404 let delivery = Delivery::new(recipient, 11);
405
406 assert_eq!(delivery.to.resolve(MailAddr(99)), MailAddr(7));
407 assert_eq!(delivery.to.resolve(MailAddr(1234)), MailAddr(7));
408 assert_eq!(delivery.message, 11);
409 }
410
411 #[test]
412 fn recipient_value_contract_distinguishes_routes() {
413 let global = Recipient::<Inbox>::global(MailAddr(7));
414 let same_global = Recipient::<Inbox>::global(MailAddr(7));
415 let other_global = Recipient::<Inbox>::global(MailAddr(8));
416 let child = DeliveryTarget::<Inbox>::LocalChild(ChildRecipient::new(3));
417 let same_child = DeliveryTarget::<Inbox>::LocalChild(ChildRecipient::new(3));
418 let other_child = DeliveryTarget::<Inbox>::LocalChild(ChildRecipient::new(4));
419
420 assert_eq!(global, same_global);
421 assert_ne!(global, other_global);
422 assert_ne!(child, global);
423 assert_eq!(child, same_child);
424 assert_ne!(child, other_child);
425 assert!(child.is_local_child(3));
426 assert!(!child.is_local_child(4));
427 assert_eq!(global.address(), MailAddr(7));
428 assert_eq!(format!("{global:?}"), "MailAddr(7)");
429 assert_eq!(format!("{child:?}"), "LocalChild(3)");
430 }
431
432 #[test]
433 fn delivery_equality_requires_both_destination_and_message() {
434 let value = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 5);
435 let same = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 5);
436 let other_destination = Delivery::<Inbox>::new(Recipient::global(MailAddr(2)), 5);
437 let other_message = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 6);
438 let both_different = Delivery::<Inbox>::new(Recipient::global(MailAddr(2)), 6);
439
440 assert!(value == same);
441 assert!(value != other_destination);
442 assert!(value != other_message);
443 assert!(value != both_different);
444 }
445}