use std::any::TypeId;
use std::fmt::Debug;
use std::marker::PhantomData;
use tokio::sync::mpsc;
use crate::actor::Actor;
use crate::endpoint::Endpoint;
use crate::receptionist::{ActorEntry, EntryLocation};
pub struct ReceptionKey<A: Actor> {
pub(crate) id: String,
_marker: PhantomData<A>,
}
impl<A: Actor> ReceptionKey<A> {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
_marker: PhantomData,
}
}
pub fn id(&self) -> &str {
&self.id
}
}
impl<A: Actor> Clone for ReceptionKey<A> {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
_marker: PhantomData,
}
}
}
impl<A: Actor> Debug for ReceptionKey<A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ReceptionKey<{}>({:?})",
std::any::type_name::<A>(),
self.id
)
}
}
#[derive(Clone, Debug)]
pub(crate) struct ErasedReceptionKey {
pub(crate) id: String,
pub(crate) type_id: TypeId,
}
pub struct Listing<A: Actor> {
rx: mpsc::UnboundedReceiver<Endpoint<A>>,
}
impl<A: Actor> Listing<A> {
pub(crate) fn new(rx: mpsc::UnboundedReceiver<Endpoint<A>>) -> Self {
Self { rx }
}
pub async fn next(&mut self) -> Option<Endpoint<A>> {
self.rx.recv().await
}
pub fn try_next(&mut self) -> Option<Endpoint<A>> {
self.rx.try_recv().ok()
}
}
pub(crate) trait ErasedListingSender: Send + Sync {
fn key_id(&self) -> &str;
fn actor_type_id(&self) -> TypeId;
fn try_send_from_entry(&self, entry: &ActorEntry) -> bool;
fn is_closed(&self) -> bool;
}
pub(crate) struct TypedListingSender<A: Actor + 'static> {
pub(crate) key_id: String,
pub(crate) tx: mpsc::UnboundedSender<Endpoint<A>>,
}
impl<A: Actor + 'static> ErasedListingSender for TypedListingSender<A> {
fn key_id(&self) -> &str {
&self.key_id
}
fn actor_type_id(&self) -> TypeId {
TypeId::of::<A>()
}
fn is_closed(&self) -> bool {
self.tx.is_closed()
}
fn try_send_from_entry(&self, entry: &ActorEntry) -> bool {
if entry.type_id != TypeId::of::<A>() {
return true; }
let endpoint = match &entry.location {
EntryLocation::Local {
endpoint_factory, ..
} => {
let any_ep = endpoint_factory.create_endpoint_any();
match any_ep.downcast::<Endpoint<A>>() {
Ok(ep) => *ep,
Err(_) => return true,
}
}
EntryLocation::Remote {
wire_tx,
response_registry,
} => Endpoint::remote(
entry.label.clone(),
wire_tx.clone(),
response_registry.clone(),
),
};
self.tx.send(endpoint).is_ok()
}
}
pub enum ListingEvent<A: Actor> {
Added {
label: String,
endpoint: Endpoint<A>,
},
Removed { label: String },
}
pub struct WatchedListing<A: Actor> {
rx: mpsc::UnboundedReceiver<ListingEvent<A>>,
}
impl<A: Actor> WatchedListing<A> {
pub(crate) fn new(rx: mpsc::UnboundedReceiver<ListingEvent<A>>) -> Self {
Self { rx }
}
pub async fn next(&mut self) -> Option<ListingEvent<A>> {
self.rx.recv().await
}
pub fn try_next(&mut self) -> Option<ListingEvent<A>> {
self.rx.try_recv().ok()
}
}
pub(crate) trait ErasedWatchedListingSender: Send + Sync {
fn key_id(&self) -> &str;
fn actor_type_id(&self) -> TypeId;
fn try_send_added(&self, entry: &ActorEntry) -> bool;
fn try_send_removed(&self, label: &str) -> bool;
fn is_closed(&self) -> bool;
}
pub(crate) struct TypedWatchedListingSender<A: Actor + 'static> {
pub(crate) key_id: String,
pub(crate) tx: mpsc::UnboundedSender<ListingEvent<A>>,
}
impl<A: Actor + 'static> ErasedWatchedListingSender for TypedWatchedListingSender<A> {
fn key_id(&self) -> &str {
&self.key_id
}
fn actor_type_id(&self) -> TypeId {
TypeId::of::<A>()
}
fn is_closed(&self) -> bool {
self.tx.is_closed()
}
fn try_send_added(&self, entry: &ActorEntry) -> bool {
if entry.type_id != TypeId::of::<A>() {
return true; }
let endpoint = match &entry.location {
EntryLocation::Local {
endpoint_factory, ..
} => {
let any_ep = endpoint_factory.create_endpoint_any();
match any_ep.downcast::<Endpoint<A>>() {
Ok(ep) => *ep,
Err(_) => return true,
}
}
EntryLocation::Remote {
wire_tx,
response_registry,
} => Endpoint::remote(
entry.label.clone(),
wire_tx.clone(),
response_registry.clone(),
),
};
self.tx
.send(ListingEvent::Added {
label: entry.label.clone(),
endpoint,
})
.is_ok()
}
fn try_send_removed(&self, label: &str) -> bool {
self.tx
.send(ListingEvent::Removed {
label: label.to_string(),
})
.is_ok()
}
}