use core::marker::PhantomData;
use crate::{ActionItem, Behavior, MessageProtocol, Never, Protocol};
pub trait Address: Copy + Eq {
type Nonce: Copy + Eq;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MailAddr(pub u64);
impl From<u64> for MailAddr {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<MailAddr> for u64 {
fn from(value: MailAddr) -> Self {
value.0
}
}
impl Address for MailAddr {
type Nonce = u64;
}
pub trait EndpointAddress: Address + Sized {
type Established<P>: Clone
where
P: Protocol<Addr = Self>;
}
pub struct Recipient<P: Protocol> {
address: P::Addr,
protocol: PhantomData<fn() -> P>,
}
impl<P: Protocol> Copy for Recipient<P> {}
impl<P: Protocol> Clone for Recipient<P> {
fn clone(&self) -> Self {
*self
}
}
impl<P: Protocol> Recipient<P> {
#[must_use]
pub fn global(address: P::Addr) -> Self {
Self::new(address)
}
#[must_use]
pub const fn address(self) -> P::Addr {
self.address
}
#[doc(hidden)]
const fn new(address: P::Addr) -> Self {
Self {
address,
protocol: PhantomData,
}
}
}
impl<A: Address, M> From<A> for Recipient<MessageProtocol<A, M>> {
fn from(address: A) -> Self {
Self::new(address)
}
}
pub struct EstablishedRecipient<P>
where
P: Protocol,
P::Addr: EndpointAddress,
{
pub(crate) endpoint: <P::Addr as EndpointAddress>::Established<P>,
}
impl<P> EstablishedRecipient<P>
where
P: Protocol,
P::Addr: EndpointAddress,
{
#[must_use]
pub const fn issued(endpoint: <P::Addr as EndpointAddress>::Established<P>) -> Self {
Self { endpoint }
}
pub fn interpret<I>(self, interpreter: &mut I) -> I::Output
where
I: InterpretEstablished<P>,
{
interpreter.interpret_established(self.endpoint)
}
}
pub trait InterpretEstablished<P>
where
P: Protocol,
P::Addr: EndpointAddress,
{
type Output;
fn interpret_established(
&mut self,
endpoint: <P::Addr as EndpointAddress>::Established<P>,
) -> Self::Output;
}
impl<P> Clone for EstablishedRecipient<P>
where
P: Protocol,
P::Addr: EndpointAddress,
{
fn clone(&self) -> Self {
Self::issued(self.endpoint.clone())
}
}
impl<P> core::fmt::Debug for EstablishedRecipient<P>
where
P: Protocol,
P::Addr: EndpointAddress,
<P::Addr as EndpointAddress>::Established<P>: core::fmt::Debug,
{
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_tuple("EstablishedRecipient")
.field(&self.endpoint)
.finish()
}
}
impl<P> PartialEq for EstablishedRecipient<P>
where
P: Protocol,
P::Addr: EndpointAddress,
<P::Addr as EndpointAddress>::Established<P>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.endpoint == other.endpoint
}
}
impl<P> Eq for EstablishedRecipient<P>
where
P: Protocol,
P::Addr: EndpointAddress,
<P::Addr as EndpointAddress>::Established<P>: Eq,
{
}
pub struct EstablishedActor<B>
where
B: Behavior,
<B::Protocol as Protocol>::Addr: EndpointAddress,
{
recipient: EstablishedRecipient<B::Protocol>,
behavior: PhantomData<fn() -> B>,
}
impl<B> EstablishedActor<B>
where
B: Behavior,
<B::Protocol as Protocol>::Addr: EndpointAddress,
{
#[must_use]
pub const fn issued(
endpoint: <<B::Protocol as Protocol>::Addr as EndpointAddress>::Established<B::Protocol>,
) -> Self {
Self {
recipient: EstablishedRecipient::issued(endpoint),
behavior: PhantomData,
}
}
pub(crate) const fn from_recipient(recipient: EstablishedRecipient<B::Protocol>) -> Self {
Self {
recipient,
behavior: PhantomData,
}
}
#[must_use]
pub fn recipient(&self) -> EstablishedRecipient<B::Protocol> {
self.recipient.clone()
}
#[must_use]
pub fn into_recipient(self) -> EstablishedRecipient<B::Protocol> {
self.recipient
}
pub fn interpret<I>(self, interpreter: &mut I) -> I::Output
where
I: InterpretEstablished<B::Protocol>,
{
self.recipient.interpret(interpreter)
}
}
impl<B> Clone for EstablishedActor<B>
where
B: Behavior,
<B::Protocol as Protocol>::Addr: EndpointAddress,
{
fn clone(&self) -> Self {
Self {
recipient: self.recipient.clone(),
behavior: PhantomData,
}
}
}
impl<B> core::fmt::Debug for EstablishedActor<B>
where
B: Behavior,
<B::Protocol as Protocol>::Addr: EndpointAddress,
<<B::Protocol as Protocol>::Addr as EndpointAddress>::Established<B::Protocol>:
core::fmt::Debug,
{
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_tuple("EstablishedActor")
.field(&self.recipient)
.finish()
}
}
impl<B> PartialEq for EstablishedActor<B>
where
B: Behavior,
<B::Protocol as Protocol>::Addr: EndpointAddress,
<<B::Protocol as Protocol>::Addr as EndpointAddress>::Established<B::Protocol>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.recipient == other.recipient
}
}
impl<B> Eq for EstablishedActor<B>
where
B: Behavior,
<B::Protocol as Protocol>::Addr: EndpointAddress,
<<B::Protocol as Protocol>::Addr as EndpointAddress>::Established<B::Protocol>: Eq,
{
}
impl<P: Protocol> PartialEq for Recipient<P> {
fn eq(&self, other: &Self) -> bool {
self.address == other.address
}
}
impl<P: Protocol> Eq for Recipient<P> {}
impl<P: Protocol> core::fmt::Debug for Recipient<P>
where
P::Addr: core::fmt::Debug,
<P::Addr as Address>::Nonce: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.address.fmt(f)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LogicalDeliveryReason {
UnknownAddress,
ClosedRecipient,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ExactDeliveryReason {
ClosedRecipient,
}
pub struct Delivery<P: Protocol> {
pub to: Recipient<P>,
pub message: P::Msg,
}
impl<P: Protocol> Delivery<P> {
#[must_use]
pub fn new(to: Recipient<P>, message: P::Msg) -> Self {
Self { to, message }
}
}
impl<P> Clone for Delivery<P>
where
P: Protocol,
P::Msg: Clone,
{
fn clone(&self) -> Self {
Self {
to: self.to,
message: self.message.clone(),
}
}
}
impl<P> PartialEq for Delivery<P>
where
P: Protocol,
P::Msg: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.to == other.to && self.message == other.message
}
}
impl<P> Eq for Delivery<P>
where
P: Protocol,
P::Msg: Eq,
{
}
impl<P> ActionItem for Delivery<P>
where
P: Protocol,
P::Addr: Send,
P::Msg: Send,
{
type Accepted = ();
type Rejection = LogicalDeliveryReason;
type Prerequisite = Never;
}
pub struct EstablishedDelivery<P>
where
P: Protocol,
P::Addr: EndpointAddress,
{
pub to: EstablishedRecipient<P>,
pub message: P::Msg,
}
impl<P> EstablishedDelivery<P>
where
P: Protocol,
P::Addr: EndpointAddress,
{
#[must_use]
pub const fn new(to: EstablishedRecipient<P>, message: P::Msg) -> Self {
Self { to, message }
}
}
impl<P> Clone for EstablishedDelivery<P>
where
P: Protocol,
P::Addr: EndpointAddress,
P::Msg: Clone,
{
fn clone(&self) -> Self {
Self::new(self.to.clone(), self.message.clone())
}
}
impl<P> core::fmt::Debug for EstablishedDelivery<P>
where
P: Protocol,
P::Addr: EndpointAddress,
EstablishedRecipient<P>: core::fmt::Debug,
P::Msg: core::fmt::Debug,
{
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("EstablishedDelivery")
.field("to", &self.to)
.field("message", &self.message)
.finish()
}
}
impl<P> PartialEq for EstablishedDelivery<P>
where
P: Protocol,
P::Addr: EndpointAddress,
EstablishedRecipient<P>: PartialEq,
P::Msg: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.to == other.to && self.message == other.message
}
}
impl<P> Eq for EstablishedDelivery<P>
where
P: Protocol,
P::Addr: EndpointAddress,
EstablishedRecipient<P>: Eq,
P::Msg: Eq,
{
}
impl<P> ActionItem for EstablishedDelivery<P>
where
P: Protocol,
P::Addr: EndpointAddress,
<P::Addr as EndpointAddress>::Established<P>: Send,
P::Msg: Send,
{
type Accepted = ();
type Rejection = ExactDeliveryReason;
type Prerequisite = Never;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Actions, Never, NoBirths, User};
use std::rc::Rc;
struct Inbox;
struct SignatureOnly;
#[derive(Clone, Copy, PartialEq, Eq)]
struct LocalAddr(u8);
impl Address for LocalAddr {
type Nonce = u8;
}
struct LocalProtocol;
impl Protocol for LocalProtocol {
type Addr = LocalAddr;
type Msg = u8;
}
struct LocalEndpoint<P> {
token: Rc<()>,
protocol: PhantomData<fn() -> P>,
}
impl<P> Clone for LocalEndpoint<P> {
fn clone(&self) -> Self {
Self {
token: self.token.clone(),
protocol: PhantomData,
}
}
}
impl<P> core::fmt::Debug for LocalEndpoint<P> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("LocalEndpoint")
}
}
impl<P> PartialEq for LocalEndpoint<P> {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.token, &other.token)
}
}
impl<P> Eq for LocalEndpoint<P> {}
impl EndpointAddress for LocalAddr {
type Established<P>
= LocalEndpoint<P>
where
P: Protocol<Addr = Self>;
}
struct LocalTransfer;
impl InterpretEstablished<LocalProtocol> for LocalTransfer {
type Output = Rc<()>;
fn interpret_established(
&mut self,
endpoint: LocalEndpoint<LocalProtocol>,
) -> Self::Output {
endpoint.token
}
}
struct LocalBehavior;
impl Behavior for LocalBehavior {
type Protocol = LocalProtocol;
type Event = User<LocalAddr, u8>;
type Sends = Vec<Never>;
type Ph = Never;
type Error = Never;
type Birth = NoBirths;
fn transition(
&mut self,
_: crate::ActiveTurn,
_: Self::Event,
) -> crate::BehaviorActed<Self> {
Ok(Actions::cont())
}
}
impl crate::Protocol for SignatureOnly {
type Addr = MailAddr;
type Msg = u8;
}
impl behavior::Protocol for Inbox {
type Addr = MailAddr;
type Msg = u8;
}
impl Behavior for Inbox {
type Protocol = Self;
type Event = User<MailAddr, u8>;
type Sends = Vec<Never>;
type Ph = Never;
type Error = Never;
type Birth = NoBirths;
fn init(&mut self, _: crate::InitializationTurn) -> crate::BehaviorActed<Self> {
Ok(Actions::cont())
}
fn transition(
&mut self,
_: crate::ActiveTurn,
_: Self::Event,
) -> crate::BehaviorActed<Self> {
Ok(Actions::cont())
}
}
#[test]
fn mail_address_conversion_preserves_nonzero_value() {
assert_eq!(u64::from(MailAddr(41)), 41);
}
#[test]
fn routing_requires_only_the_static_protocol_signature() {
let recipient = Recipient::<SignatureOnly>::global(MailAddr(7));
let delivery = Delivery::new(recipient, 11);
assert_eq!(delivery.to.address(), MailAddr(7));
assert_eq!(delivery.message, 11);
}
#[test]
fn recipient_value_contract_distinguishes_logical_addresses() {
let global = Recipient::<Inbox>::global(MailAddr(7));
let same_global = Recipient::<Inbox>::global(MailAddr(7));
let other_global = Recipient::<Inbox>::global(MailAddr(8));
assert_eq!(global, same_global);
assert_ne!(global, other_global);
assert_eq!(global.address(), MailAddr(7));
assert_eq!(format!("{global:?}"), "MailAddr(7)");
}
#[test]
fn established_capability_accepts_a_local_non_send_endpoint() {
let token = Rc::new(());
let recipient = EstablishedRecipient::<LocalProtocol>::issued(LocalEndpoint {
token: token.clone(),
protocol: PhantomData,
});
let retained = recipient.clone();
let extracted = retained.interpret(&mut LocalTransfer);
assert!(Rc::ptr_eq(&token, &extracted));
drop(recipient);
}
#[test]
fn established_values_compare_complete_identity_and_expose_debug_shape() {
let token = Rc::new(());
let other_token = Rc::new(());
let recipient = EstablishedRecipient::<LocalProtocol>::issued(LocalEndpoint {
token: token.clone(),
protocol: PhantomData,
});
let same_recipient = EstablishedRecipient::<LocalProtocol>::issued(LocalEndpoint {
token,
protocol: PhantomData,
});
let other_recipient = EstablishedRecipient::<LocalProtocol>::issued(LocalEndpoint {
token: other_token,
protocol: PhantomData,
});
assert_eq!(recipient, same_recipient);
assert_ne!(recipient, other_recipient);
assert_eq!(
format!("{recipient:?}"),
"EstablishedRecipient(LocalEndpoint)"
);
let actor = EstablishedActor::<LocalBehavior>::from_recipient(recipient.clone());
let same_actor = EstablishedActor::<LocalBehavior>::from_recipient(same_recipient.clone());
let other_actor =
EstablishedActor::<LocalBehavior>::from_recipient(other_recipient.clone());
assert_eq!(actor, same_actor);
assert_ne!(actor, other_actor);
assert_eq!(
format!("{actor:?}"),
"EstablishedActor(EstablishedRecipient(LocalEndpoint))"
);
let delivery = EstablishedDelivery::new(recipient, 7);
let same_delivery = EstablishedDelivery::new(same_recipient.clone(), 7);
let other_destination = EstablishedDelivery::new(other_recipient.clone(), 7);
let other_message = EstablishedDelivery::new(same_recipient, 9);
let both_different = EstablishedDelivery::new(other_recipient, 9);
assert_eq!(delivery, same_delivery);
assert_ne!(delivery, other_destination);
assert_ne!(delivery, other_message);
assert_ne!(delivery, both_different);
assert_eq!(
format!("{delivery:?}"),
"EstablishedDelivery { to: EstablishedRecipient(LocalEndpoint), message: 7 }"
);
}
#[test]
fn delivery_equality_requires_both_destination_and_message() {
let value = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 5);
let same = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 5);
let other_destination = Delivery::<Inbox>::new(Recipient::global(MailAddr(2)), 5);
let other_message = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 6);
let both_different = Delivery::<Inbox>::new(Recipient::global(MailAddr(2)), 6);
assert!(value == same);
assert!(value != other_destination);
assert!(value != other_message);
assert!(value != both_different);
}
}