use std::fmt::{self, Debug};
use bytes::Bytes;
use thiserror::Error;
use acktor::{
Actor, ActorState, Address, HasStableTypeId, Message, MessageId, Recipient, Sender,
channel::oneshot, utils::ShortName,
};
use crate::codec::DecodeContext;
use crate::remote_address::RemoteAddress;
pub struct RemoteMessage {
pub actor_id: u64,
pub message_id: u64,
pub message: Bytes,
pub result_tx: Option<oneshot::Sender<Bytes>>,
pub decode_context: Option<DecodeContext>,
}
impl Debug for RemoteMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RemoteMessage")
.field("actor_id", &self.actor_id)
.field("message_id", &self.message_id)
.field("message", &format_args!("Bytes({})", self.message.len()))
.field(
"result_tx",
&match self.result_tx {
Some(_) => format_args!("Send"),
None => format_args!("DoSend"),
},
)
.finish()
}
}
impl Message for RemoteMessage {
type Result = ();
}
impl RemoteMessage {
pub fn do_send(actor_id: u64, message_id: u64, message: Bytes) -> Self {
Self {
actor_id,
message_id,
message,
result_tx: None,
decode_context: None,
}
}
pub fn send(
actor_id: u64,
message_id: u64,
message: Bytes,
tx: oneshot::Sender<Bytes>,
) -> Self {
Self {
actor_id,
message_id,
message,
result_tx: Some(tx),
decode_context: None,
}
}
pub fn with_context(mut self, context: DecodeContext) -> Self {
self.decode_context = Some(context);
self
}
}
#[derive(Debug, Error)]
pub enum ToRemoteMessageRecipientError {
#[error(
"`{0}` does not opt-in the `type-erased-recipient-hook` feature, see the docs of \
`RemoteActor` for details"
)]
MissingHook(String),
#[error("{0}")]
DowncastFailed(String),
}
pub trait ToRemoteMessageRecipient {
fn to_remote_message_recipient(
&self,
) -> Result<Recipient<RemoteMessage>, ToRemoteMessageRecipientError>;
}
impl<A> ToRemoteMessageRecipient for Address<A>
where
A: Actor,
{
fn to_remote_message_recipient(
&self,
) -> Result<Recipient<RemoteMessage>, ToRemoteMessageRecipientError> {
Ok(*self
.type_erased_recipient()
.ok_or_else(|| {
ToRemoteMessageRecipientError::MissingHook(ShortName::of::<A>().to_string())
})?
.downcast::<Recipient<RemoteMessage>>()
.map_err(|(_, e)| ToRemoteMessageRecipientError::DowncastFailed(e))?)
}
}
impl<M> ToRemoteMessageRecipient for Recipient<M>
where
M: Message,
{
fn to_remote_message_recipient(
&self,
) -> Result<Recipient<RemoteMessage>, ToRemoteMessageRecipientError> {
Ok(*self
.type_erased_recipient()
.ok_or_else(|| {
ToRemoteMessageRecipientError::MissingHook(
ShortName::of::<Recipient<M>>().to_string(),
)
})?
.downcast::<Recipient<RemoteMessage>>()
.map_err(|(_, e)| ToRemoteMessageRecipientError::DowncastFailed(e))?)
}
}
#[derive(Debug)]
pub enum RemoteSupervisionEvent {
Warn(RemoteAddress, String),
Terminated(RemoteAddress, Option<String>),
Panicked(RemoteAddress, String),
State(RemoteAddress, ActorState),
}
impl Message for RemoteSupervisionEvent {
type Result = ();
}
impl HasStableTypeId for RemoteSupervisionEvent {
const STABLE_TYPE_ID: acktor::StableTypeId = acktor::StableTypeId::from_stable_type_name(
concat!(module_path!(), "::", stringify!(RemoteSupervisionEvent)),
);
}
impl MessageId for RemoteSupervisionEvent {
const ID: u64 =
acktor::StableTypeId::from_stable_type_name("acktor::supervisor::SupervisionEvent")
.as_u64();
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn test_debug_fmt() {
let msg = RemoteMessage::do_send(7, 99, Bytes::from_static(b"hello"));
assert_eq!(
format!("{msg:?}"),
"RemoteMessage { actor_id: 7, message_id: 99, message: Bytes(5), result_tx: DoSend }"
);
let (tx, _rx) = oneshot::channel::<Bytes>();
let msg = RemoteMessage::send(7, 99, Bytes::from_static(b"hi"), tx);
assert_eq!(
format!("{msg:?}"),
"RemoteMessage { actor_id: 7, message_id: 99, message: Bytes(2), result_tx: Send }"
);
}
}