use core::future::Future;
use core::marker::PhantomData;
use super::addressing::{Address, ChildRecipient};
use crate::next::Never;
use crate::{Behavior, Protocol};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CreationKind<N> {
Birth,
ReplacementIncarnation {
replaces: N,
},
}
impl<N> CreationKind<N> {
#[must_use]
pub const fn replacement_of(replaces: N) -> Self {
Self::ReplacementIncarnation { replaces }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Create<A: Address, New> {
pub nonce: A::Nonce,
pub child: New,
pub kind: CreationKind<A::Nonce>,
}
impl<A: Address, New> Create<A, New> {
#[must_use]
pub const fn new(nonce: A::Nonce, child: New, kind: CreationKind<A::Nonce>) -> Self {
Self { nonce, child, kind }
}
#[must_use]
pub const fn birth(nonce: A::Nonce, child: New) -> Self {
Self::new(nonce, child, CreationKind::Birth)
}
#[must_use]
pub const fn replacement_incarnation(nonce: A::Nonce, replaces: A::Nonce, child: New) -> Self {
Self::new(nonce, child, CreationKind::replacement_of(replaces))
}
}
pub struct ChildRoute<Child: Behavior, Role> {
recipient: ChildRecipient<Child::Protocol>,
child: PhantomData<fn() -> Child>,
role: PhantomData<fn() -> Role>,
}
pub trait ChildRole<Parent: Behavior> {
type Child: Behavior;
type Position: ChildPosition<<Parent::Birth as BirthMode>::Child, Self::Child>;
}
impl<Child: Behavior, Role> Copy for ChildRoute<Child, Role> {}
impl<Child: Behavior, Role> Clone for ChildRoute<Child, Role> {
fn clone(&self) -> Self {
*self
}
}
impl<Child: Behavior, Role> PartialEq for ChildRoute<Child, Role> {
fn eq(&self, other: &Self) -> bool {
self.recipient == other.recipient
}
}
impl<Child: Behavior, Role> Eq for ChildRoute<Child, Role> {}
impl<Child: Behavior, Role> core::fmt::Debug for ChildRoute<Child, Role>
where
<<Child::Protocol as Protocol>::Addr as Address>::Nonce: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ChildRoute")
.field("nonce", &self.nonce())
.finish()
}
}
impl<Child: Behavior, Role> ChildRoute<Child, Role> {
#[must_use]
pub const fn new(nonce: <<Child::Protocol as Protocol>::Addr as Address>::Nonce) -> Self {
Self {
recipient: ChildRecipient::new(nonce),
child: PhantomData,
role: PhantomData,
}
}
#[must_use]
pub const fn recipient(self) -> ChildRecipient<Child::Protocol> {
self.recipient
}
#[must_use]
pub const fn nonce(self) -> <<Child::Protocol as Protocol>::Addr as Address>::Nonce {
self.recipient.nonce()
}
#[must_use]
pub const fn stage(
self,
child: Child,
kind: CreationKind<<<Child::Protocol as Protocol>::Addr as Address>::Nonce>,
) -> Create<<Child::Protocol as Protocol>::Addr, Child> {
Create::new(self.nonce(), child, kind)
}
#[must_use]
pub const fn birth(self, child: Child) -> Create<<Child::Protocol as Protocol>::Addr, Child> {
self.stage(child, CreationKind::Birth)
}
#[must_use]
pub const fn replacement_incarnation(
self,
replaces: <<Child::Protocol as Protocol>::Addr as Address>::Nonce,
child: Child,
) -> Create<<Child::Protocol as Protocol>::Addr, Child> {
self.stage(child, CreationKind::replacement_of(replaces))
}
}
pub trait InstallBirth<A: Address, C: Behavior, Output, Error>
where
C::Protocol: Protocol<Addr = A>,
{
fn install_birth(
&mut self,
creation: Create<A, C>,
) -> impl Future<Output = Result<Output, Error>> + Send;
}
pub trait DispatchBirth<A: Address, Installer, Output, Error>: Sized {
fn dispatch_birth(
self,
nonce: A::Nonce,
kind: CreationKind<A::Nonce>,
installer: &mut Installer,
) -> impl Future<Output = Result<Output, Error>> + Send;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChildChoice<Head, Tail> {
Head(Head),
Tail(Tail),
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ChildHead;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ChildTail<Position>(PhantomData<fn() -> Position>);
pub trait ChildPosition<Children, Child: Behavior>: sealed::ChildPosition {}
impl sealed::ChildPosition for ChildHead {}
impl<Head: Behavior, Tail> ChildPosition<ChildChoice<Head, Tail>, Head> for ChildHead {}
impl<Position> sealed::ChildPosition for ChildTail<Position> {}
impl<Head, Tail, Position, Child> ChildPosition<ChildChoice<Head, Tail>, Child>
for ChildTail<Position>
where
Child: Behavior,
Position: ChildPosition<Tail, Child>,
{
}
mod sealed {
pub trait ChildPosition {}
pub trait ChildProduct {}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct NoChildren;
impl sealed::ChildProduct for NoChildren {}
pub struct ChildCons<A: Address, C, Earlier> {
creation: Create<A, C>,
earlier: Earlier,
}
impl<A: Address, C, Earlier> sealed::ChildProduct for ChildCons<A, C, Earlier> {}
pub struct Children<A: Address, Product = NoChildren> {
product: Product,
address: PhantomData<fn() -> A>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum ChildrenError<N> {
#[error("duplicate creator-local child nonce")]
DuplicateNonce {
nonce: N,
},
}
pub trait ChildProduct<A: Address>: sealed::ChildProduct + Sized {
type Choice;
#[doc(hidden)]
fn stage(
self,
) -> Result<(Vec<Create<A, Self::Choice>>, Vec<A::Nonce>), ChildrenError<A::Nonce>>;
}
impl<A: Address> ChildProduct<A> for NoChildren {
type Choice = Never;
fn stage(
self,
) -> Result<(Vec<Create<A, Self::Choice>>, Vec<A::Nonce>), ChildrenError<A::Nonce>> {
Ok((Vec::new(), Vec::new()))
}
}
impl<A, C, Earlier> ChildProduct<A> for ChildCons<A, C, Earlier>
where
A: Address,
C: Behavior,
C::Protocol: Protocol<Addr = A>,
Earlier: ChildProduct<A>,
{
type Choice = ChildChoice<C, Earlier::Choice>;
fn stage(
self,
) -> Result<(Vec<Create<A, Self::Choice>>, Vec<A::Nonce>), ChildrenError<A::Nonce>> {
let (earlier, mut nonces) = self.earlier.stage()?;
if nonces.contains(&self.creation.nonce) {
return Err(ChildrenError::DuplicateNonce {
nonce: self.creation.nonce,
});
}
let mut creates = earlier
.into_iter()
.map(|creation| {
Create::new(
creation.nonce,
ChildChoice::Tail(creation.child),
creation.kind,
)
})
.collect::<Vec<_>>();
nonces.push(self.creation.nonce);
creates.push(Create::new(
self.creation.nonce,
ChildChoice::Head(self.creation.child),
self.creation.kind,
));
Ok((creates, nonces))
}
}
impl<A: Address> Children<A, NoChildren> {
#[must_use]
pub const fn new() -> Self {
Self {
product: NoChildren,
address: PhantomData,
}
}
}
impl<A: Address> Default for Children<A, NoChildren> {
fn default() -> Self {
Self::new()
}
}
impl<A: Address, Product> Children<A, Product> {
#[must_use]
pub fn create<C>(self, creation: Create<A, C>) -> Children<A, ChildCons<A, C, Product>>
where
C: Behavior,
C::Protocol: Protocol<Addr = A>,
{
Children {
product: ChildCons {
creation,
earlier: self.product,
},
address: PhantomData,
}
}
#[must_use]
pub fn child<C>(self, nonce: A::Nonce, child: C) -> Children<A, ChildCons<A, C, Product>>
where
C: Behavior,
C::Protocol: Protocol<Addr = A>,
{
self.create(Create::birth(nonce, child))
}
#[must_use]
pub fn child_at<C, Role>(
self,
route: ChildRoute<C, Role>,
child: C,
) -> Children<A, ChildCons<A, C, Product>>
where
C: Behavior,
C::Protocol: Protocol<Addr = A>,
{
self.create(route.birth(child))
}
}
impl<A, Product> Children<A, Product>
where
A: Address,
Product: ChildProduct<A>,
{
pub fn into_creates(self) -> Result<Vec<Create<A, Product::Choice>>, ChildrenError<A::Nonce>> {
self.product.stage().map(|(creates, _)| creates)
}
}
impl<A, Head, Tail, Installer, Output, Error> DispatchBirth<A, Installer, Output, Error>
for ChildChoice<Head, Tail>
where
A: Address,
A::Nonce: Send,
Head: Behavior + Send,
Head::Protocol: Protocol<Addr = A>,
Tail: DispatchBirth<A, Installer, Output, Error> + Send,
Installer: InstallBirth<A, Head, Output, Error> + Send,
{
async fn dispatch_birth(
self,
nonce: A::Nonce,
kind: CreationKind<A::Nonce>,
installer: &mut Installer,
) -> Result<Output, Error> {
match self {
Self::Head(child) => {
installer
.install_birth(Create::new(nonce, child, kind))
.await
}
Self::Tail(tail) => tail.dispatch_birth(nonce, kind, installer).await,
}
}
}
impl<A, Installer, Output, Error> DispatchBirth<A, Installer, Output, Error> for Never
where
A: Address,
{
fn dispatch_birth(
self,
_nonce: A::Nonce,
_kind: CreationKind<A::Nonce>,
_installer: &mut Installer,
) -> impl Future<Output = Result<Output, Error>> + Send {
async move { match self {} }
}
}
impl<A, C, Installer, Output, Error> DispatchBirth<A, Installer, Output, Error> for C
where
A: Address,
C: Behavior,
C::Protocol: Protocol<Addr = A>,
Installer: InstallBirth<A, C, Output, Error>,
{
fn dispatch_birth(
self,
nonce: A::Nonce,
kind: CreationKind<A::Nonce>,
installer: &mut Installer,
) -> impl Future<Output = Result<Output, Error>> + Send {
installer.install_birth(Create::new(nonce, self, kind))
}
}
pub trait BirthMode {
type Child;
}
pub struct NoBirthProtocols;
pub struct BirthProtocol<P: Protocol, Tail> {
protocol: PhantomData<fn() -> P>,
tail: PhantomData<fn() -> Tail>,
}
pub struct BirthProtocolHead;
pub struct BirthProtocolTail<Position>(PhantomData<fn() -> Position>);
pub trait BirthProtocolAt<P: Protocol, Position> {}
impl<P: Protocol, Tail> BirthProtocolAt<P, BirthProtocolHead> for BirthProtocol<P, Tail> {}
impl<Head, Tail, P, Position> BirthProtocolAt<P, BirthProtocolTail<Position>>
for BirthProtocol<Head, Tail>
where
Head: Protocol,
P: Protocol,
Tail: BirthProtocolAt<P, Position>,
{
}
#[doc(hidden)]
pub trait BirthProtocolProduct {
type Append<Tail: BirthProtocolProduct>: BirthProtocolProduct;
}
impl BirthProtocolProduct for NoBirthProtocols {
type Append<Tail: BirthProtocolProduct> = Tail;
}
impl<P, Rest> BirthProtocolProduct for BirthProtocol<P, Rest>
where
P: Protocol,
Rest: BirthProtocolProduct,
{
type Append<Tail: BirthProtocolProduct> = BirthProtocol<P, Rest::Append<Tail>>;
}
pub trait BirthProtocols: Behavior {
type Protocols: BirthProtocolProduct;
}
impl<B> BirthProtocols for B
where
B: Behavior,
B::Birth: BirthModeProtocols,
{
type Protocols = BirthProtocol<B::Protocol, <B::Birth as BirthModeProtocols>::Protocols>;
}
#[doc(hidden)]
pub trait BirthModeProtocols {
type Protocols: BirthProtocolProduct;
}
impl<M> BirthModeProtocols for M
where
M: BirthMode,
M::Child: BirthNodeProtocols,
{
type Protocols = <M::Child as BirthNodeProtocols>::Protocols;
}
#[doc(hidden)]
pub trait BirthNodeProtocols {
type Protocols: BirthProtocolProduct;
}
impl<B> BirthNodeProtocols for B
where
B: BirthProtocols,
{
type Protocols = B::Protocols;
}
impl<Head, Tail> BirthNodeProtocols for ChildChoice<Head, Tail>
where
Head: BirthNodeProtocols,
Tail: BirthNodeProtocols,
{
type Protocols = <Head::Protocols as BirthProtocolProduct>::Append<Tail::Protocols>;
}
impl BirthNodeProtocols for Never {
type Protocols = NoBirthProtocols;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct NoBirths;
impl BirthMode for NoBirths {
type Child = Never;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Births<C>(PhantomData<fn() -> C>);
impl<C> BirthMode for Births<C> {
type Child = C;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Actions, BehaviorActed, MailAddr, NoBirths, User};
struct Child;
struct SharedProtocol;
impl behavior::Protocol for SharedProtocol {
type Addr = MailAddr;
type Msg = u8;
}
struct Primary;
struct Fallback;
macro_rules! shared_protocol_behavior {
($behavior:ty, $birth:ty) => {
impl Behavior for $behavior {
type Protocol = SharedProtocol;
type Event = User<MailAddr, u8>;
type Sends = Vec<Never>;
type Ph = Never;
type Error = Never;
type Birth = $birth;
fn transition(
&mut self,
_: crate::ActiveTurn,
_: Self::Event,
) -> BehaviorActed<Self> {
Ok(Actions::cont())
}
}
};
}
shared_protocol_behavior!(Primary, Births<Child>);
shared_protocol_behavior!(Fallback, NoBirths);
impl behavior::Protocol for Child {
type Addr = MailAddr;
type Msg = u8;
}
impl Behavior for Child {
type Protocol = Self;
type Event = User<MailAddr, u8>;
type Sends = Vec<Never>;
type Ph = Never;
type Error = Never;
type Birth = NoBirths;
fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> BehaviorActed<Self> {
Ok(Actions::cont())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum InstallError {
Refused,
}
struct RecordingInstaller {
calls: usize,
observed: Vec<(u64, CreationKind<u64>)>,
result: Result<u32, InstallError>,
}
#[derive(Debug, PartialEq, Eq)]
enum SharedInstallation {
Primary(u64),
Fallback(u64),
}
#[derive(Default)]
struct SharedProtocolInstaller(Vec<SharedInstallation>);
impl InstallBirth<MailAddr, Primary, (), Never> for SharedProtocolInstaller {
async fn install_birth(
&mut self,
creation: Create<MailAddr, Primary>,
) -> Result<(), Never> {
self.0.push(SharedInstallation::Primary(creation.nonce));
Ok(())
}
}
impl InstallBirth<MailAddr, Fallback, (), Never> for SharedProtocolInstaller {
async fn install_birth(
&mut self,
creation: Create<MailAddr, Fallback>,
) -> Result<(), Never> {
self.0.push(SharedInstallation::Fallback(creation.nonce));
Ok(())
}
}
impl InstallBirth<MailAddr, Child, u32, InstallError> for RecordingInstaller {
async fn install_birth(
&mut self,
creation: Create<MailAddr, Child>,
) -> Result<u32, InstallError> {
self.calls += 1;
self.observed.push((creation.nonce, creation.kind));
self.result
}
}
fn installer(result: Result<u32, InstallError>) -> RecordingInstaller {
RecordingInstaller {
calls: 0,
observed: Vec::new(),
result,
}
}
fn assert_send<T: Send>(_: &T) {}
enum WorkerRole {}
#[test]
fn child_route_preserves_one_nonce_across_routing_and_creation_provenance() {
let route = ChildRoute::<Child, WorkerRole>::new(17);
assert_eq!(route.nonce(), 17);
assert_eq!(route.recipient().nonce(), 17);
assert_eq!(route, ChildRoute::new(17));
assert_ne!(route, ChildRoute::new(19));
assert_eq!(format!("{route:?}"), "ChildRoute { nonce: 17 }");
let birth = route.birth(Child);
assert_eq!(birth.nonce, 17);
assert_eq!(birth.kind, CreationKind::Birth);
let replacement = route.replacement_incarnation(11, Child);
assert_eq!(replacement.nonce, 17);
assert_eq!(replacement.kind, CreationKind::replacement_of(11));
}
#[test]
fn empty_child_product_stages_no_creations_or_nonces() {
let (creations, nonces) = <NoChildren as ChildProduct<MailAddr>>::stage(NoChildren)
.expect("an empty child product is valid");
assert!(creations.is_empty());
assert!(nonces.is_empty());
}
#[tokio::test]
async fn concrete_child_dispatches_once_with_exact_nonce_provenance_and_output() {
let mut installer = installer(Ok(91));
let future = Child.dispatch_birth(
17,
CreationKind::ReplacementIncarnation { replaces: 8 },
&mut installer,
);
assert_send(&future);
let result = future.await;
assert_eq!(result, Ok(91));
assert_eq!(installer.calls, 1);
assert_eq!(
installer.observed,
[(17, CreationKind::ReplacementIncarnation { replaces: 8 })]
);
}
#[tokio::test]
async fn concrete_child_returns_the_exact_installer_error_without_retry() {
let mut installer = installer(Err(InstallError::Refused));
let future = Child.dispatch_birth(23, CreationKind::replacement_of(19), &mut installer);
assert_send(&future);
let result = future.await;
assert_eq!(result, Err(InstallError::Refused));
assert_eq!(installer.calls, 1);
assert_eq!(installer.observed, [(23, CreationKind::replacement_of(19))]);
}
#[tokio::test]
async fn distinct_child_alternatives_preserve_one_canonical_protocol_identity() {
type Alternatives = ChildChoice<Primary, ChildChoice<Fallback, Never>>;
fn requires_shared_protocol<C: Behavior<Protocol = SharedProtocol>>() {}
requires_shared_protocol::<Primary>();
requires_shared_protocol::<Fallback>();
let mut installer = SharedProtocolInstaller::default();
Alternatives::Head(Primary)
.dispatch_birth(11, CreationKind::Birth, &mut installer)
.await
.unwrap();
Alternatives::Tail(ChildChoice::Head(Fallback))
.dispatch_birth(17, CreationKind::Birth, &mut installer)
.await
.unwrap();
assert_eq!(
installer.0,
[
SharedInstallation::Primary(11),
SharedInstallation::Fallback(17),
]
);
}
#[test]
fn birth_protocol_projection_recurses_without_inspecting_send_lanes() {
type Protocols = <Primary as BirthProtocols>::Protocols;
type Expected = BirthProtocol<SharedProtocol, BirthProtocol<Child, NoBirthProtocols>>;
trait Same<T> {}
impl<T> Same<T> for T {}
fn exact<T: Same<Expected>>() {}
exact::<Protocols>();
}
}