use crate::actor::Address;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Here;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Inside<Path>(core::marker::PhantomData<fn() -> Path>);
pub struct Ingress<Input, Path> {
marker: core::marker::PhantomData<fn(Input, Path)>,
}
impl<Input, Path> Copy for Ingress<Input, Path> {}
impl<Input, Path> Clone for Ingress<Input, Path> {
fn clone(&self) -> Self {
*self
}
}
impl<Input, Path> Ingress<Input, Path> {
#[must_use]
pub const fn new() -> Self {
Self {
marker: core::marker::PhantomData,
}
}
#[must_use]
pub fn event<Event>(self, input: Input) -> Event
where
Event: InjectEvent<Input, Path>,
{
Event::inject_at(input)
}
#[must_use]
pub const fn inside(self) -> Ingress<Input, Inside<Path>> {
Ingress {
marker: core::marker::PhantomData,
}
}
}
impl<Input, Path> Default for Ingress<Input, Path> {
fn default() -> Self {
Self::new()
}
}
impl<Input, Path> core::fmt::Debug for Ingress<Input, Path> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("Ingress")
}
}
impl<Input, Path> PartialEq for Ingress<Input, Path> {
fn eq(&self, _: &Self) -> bool {
true
}
}
impl<Input, Path> Eq for Ingress<Input, Path> {}
pub trait EventIngress<Source, Input>: Sized {
fn ingress(input: Input) -> Self;
}
pub trait ChildInputIngress<Source, Input>: Sized {
fn child_input(input: Input) -> Self;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EventLayer<Owned, Inner> {
Owned(Owned),
Inner(Inner),
}
impl<Owned, Inner, Source, Input> EventIngress<Source, Input> for EventLayer<Owned, Inner>
where
Inner: EventIngress<Source, Input>,
{
fn ingress(input: Input) -> Self {
Self::Inner(Inner::ingress(input))
}
}
pub trait ComposedEvent: UserEvent {
type Inner: UserEvent<Addr = Self::Addr, Message = Self::Message>;
fn from_inner(event: Self::Inner) -> Self;
}
impl<Owned, Inner> ComposedEvent for EventLayer<Owned, Inner>
where
Inner: UserEvent,
{
type Inner = Inner;
fn from_inner(event: Inner) -> Self {
Self::Inner(event)
}
}
pub trait InjectEvent<Input, Path>: Sized {
fn inject_at(input: Input) -> Self;
}
pub trait RecoverEvent<Input, Path>: Sized {
fn recover(event: Self) -> Result<Input, Self>;
}
impl<Owned, Inner> InjectEvent<Owned, Here> for EventLayer<Owned, Inner> {
fn inject_at(input: Owned) -> Self {
Self::Owned(input)
}
}
impl<Owned, Inner> RecoverEvent<Owned, Here> for EventLayer<Owned, Inner> {
fn recover(event: Self) -> Result<Owned, Self> {
match event {
Self::Owned(input) => Ok(input),
other @ Self::Inner(_) => Err(other),
}
}
}
impl<Owned, Inner, Input, Path> InjectEvent<Input, Inside<Path>> for EventLayer<Owned, Inner>
where
Inner: InjectEvent<Input, Path>,
{
fn inject_at(input: Input) -> Self {
Self::Inner(Inner::inject_at(input))
}
}
impl<Owned, Inner, Input, Path> RecoverEvent<Input, Inside<Path>> for EventLayer<Owned, Inner>
where
Inner: RecoverEvent<Input, Path>,
{
fn recover(event: Self) -> Result<Input, Self> {
match event {
Self::Inner(inner) => Inner::recover(inner).map_err(Self::Inner),
other @ Self::Owned(_) => Err(other),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct User<A, M> {
pub from: A,
pub message: M,
}
impl<A, M> User<A, M> {
#[must_use]
pub const fn new(from: A, message: M) -> Self {
Self { from, message }
}
}
impl<A, M> From<(A, M)> for User<A, M> {
fn from((from, message): (A, M)) -> Self {
Self::new(from, message)
}
}
pub trait UserEvent: Sized {
type Addr: Address;
type Message;
fn user(from: Self::Addr, message: Self::Message) -> Self;
fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self>;
}
impl<A: Address, M> UserEvent for User<A, M> {
type Addr = A;
type Message = M;
fn user(from: A, message: M) -> Self {
Self::new(from, message)
}
fn into_user(self) -> Result<Self, Self> {
Ok(self)
}
}
impl<Owned, Inner> UserEvent for EventLayer<Owned, Inner>
where
Inner: UserEvent,
{
type Addr = Inner::Addr;
type Message = Inner::Message;
fn user(from: Self::Addr, message: Self::Message) -> Self {
Self::Inner(Inner::user(from, message))
}
fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self> {
match self {
Self::Inner(inner) => inner.into_user().map_err(Self::Inner),
owned @ Self::Owned(_) => Err(owned),
}
}
}
#[cfg(test)]
mod structural_tests {
use super::*;
use crate::MailAddr;
type Nested = EventLayer<u8, EventLayer<u16, User<MailAddr, ()>>>;
#[test]
fn paths_select_exactly_one_layer_without_runtime_search() {
let outer = <Nested as InjectEvent<u8, Here>>::inject_at(3);
assert_eq!(outer, EventLayer::Owned(3));
let inner = <Nested as InjectEvent<u16, Inside<Here>>>::inject_at(5);
assert_eq!(inner, EventLayer::Inner(EventLayer::Owned(5)));
}
#[test]
fn duplicate_payload_types_remain_distinct_ownership_capabilities() {
type Duplicate = EventLayer<u8, EventLayer<u8, User<MailAddr, ()>>>;
let outer = <Duplicate as InjectEvent<u8, Here>>::inject_at(7);
let inner = <Duplicate as InjectEvent<u8, Inside<Here>>>::inject_at(7);
assert_eq!(outer, EventLayer::Owned(7));
assert_eq!(inner, EventLayer::Inner(EventLayer::Owned(7)));
}
#[test]
fn a_unique_structural_path_is_inferred_at_a_concrete_composition() {
let event = <Nested as InjectEvent<u16, _>>::inject_at(9);
assert_eq!(event, EventLayer::Inner(EventLayer::Owned(9)));
}
#[test]
fn ingress_identity_lifts_without_payload_or_runtime_route_search() {
type Inner = EventLayer<u16, User<MailAddr, ()>>;
type Outer = EventLayer<u8, Inner>;
let inner = Ingress::<u16, Here>::new();
let outer: Ingress<u16, Inside<Here>> = inner.inside();
let event: Outer = outer.event(12);
assert_eq!(event, EventLayer::Inner(EventLayer::Owned(12)));
}
#[test]
#[allow(
clippy::clone_on_copy,
reason = "the contract test independently exercises both promised construction traits"
)]
fn ingress_is_a_copyable_equal_capability_with_stable_debug_identity() {
let ingress = Ingress::<u16, Here>::new();
let copied = ingress;
let cloned = ingress.clone();
assert_eq!(ingress, copied);
assert_eq!(ingress, cloned);
assert_eq!(format!("{ingress:?}"), "Ingress");
}
}