use core::future::Future;
use core::marker::PhantomData;
use super::addressing::Address;
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 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),
}
mod sealed {
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))
}
}
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;
}
#[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;
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>,
}
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) {}
#[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))]);
}
}