use std::{any, borrow::Cow, error::Error};
use tracing::{debug, enabled, error, warn, Level};
use crate::{err::PanicErr, reason::ActorStopReason, ActorRef};
pub type BoxError = Box<dyn Error + Send + Sync + 'static>;
#[allow(async_fn_in_trait)]
pub trait Actor: Sized {
type Ref: ActorRef;
fn name(&self) -> Cow<'_, str> {
Cow::Borrowed(any::type_name::<Self>())
}
fn actor_ref(&self) -> Self::Ref {
match Self::try_actor_ref() {
Some(actor_ref) => actor_ref,
None => panic!("actor_ref called outside the scope of an actor"),
}
}
fn try_actor_ref() -> Option<Self::Ref> {
Self::Ref::current()
}
fn max_concurrent_reads() -> usize {
num_cpus::get()
}
async fn on_start(&mut self) -> Result<(), BoxError> {
if enabled!(Level::DEBUG) {
let id = self.actor_ref().id();
let name = self.name();
debug!("starting actor {name} ({id})");
}
Ok(())
}
async fn on_panic(&mut self, err: PanicErr) -> Result<Option<ActorStopReason>, BoxError> {
Ok(Some(ActorStopReason::Panicked(err)))
}
async fn on_stop(self, reason: ActorStopReason) -> Result<(), BoxError> {
let id = self.actor_ref().id();
let name = self.name();
match reason {
ActorStopReason::Normal => {
debug!("actor {name} ({id}) stopped normally");
}
ActorStopReason::Killed => {
debug!("actor {name} ({id}) was killed");
}
ActorStopReason::Panicked(err) => {
err.with(|any| {
let s = any
.downcast_ref::<&'static str>()
.copied()
.or_else(|| any.downcast_ref::<String>().map(String::as_str));
if let Some(s) = s {
error!("actor {name} ({id}) panicked: {s}");
return;
}
let box_err = any.downcast_ref::<BoxError>();
if let Some(err) = box_err {
error!("actor {name} ({id}) panicked: {err}");
}
})
.ok()
.unwrap_or_else(|| {
error!("actor {name} ({id}) panicked");
});
}
ActorStopReason::LinkDied {
id: link_id,
reason,
} => {
warn!("actor {name} ({id}) was killed due to link ({link_id}) died with reason: {reason}");
}
}
Ok(())
}
async fn on_link_died(
&mut self,
#[allow(unused)] id: u64,
reason: ActorStopReason,
) -> Result<Option<ActorStopReason>, BoxError> {
match &reason {
ActorStopReason::Normal => Ok(None),
ActorStopReason::Killed
| ActorStopReason::Panicked(_)
| ActorStopReason::LinkDied { .. } => Ok(Some(reason)),
}
}
}