use crate::Pid;
use std::any::Any;
use std::fmt;
#[cfg(feature = "telemetry")]
use tracing::Span;
pub type Message = Box<dyn Any + Send + 'static>;
#[derive(Debug, Clone)]
pub enum Signal {
Exit { from: Pid, reason: ExitReason },
Down {
reference: MonitorRef,
pid: Pid,
reason: ExitReason,
},
Stop,
Kill,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExitReason {
Normal,
Killed,
Panic(String),
Shutdown,
Custom(String),
}
impl ExitReason {
pub fn is_normal(&self) -> bool {
matches!(self, ExitReason::Normal)
}
pub fn is_trappable(&self) -> bool {
!matches!(self, ExitReason::Killed)
}
}
impl fmt::Display for ExitReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExitReason::Normal => write!(f, "normal"),
ExitReason::Killed => write!(f, "killed"),
ExitReason::Panic(msg) => write!(f, "panic: {}", msg),
ExitReason::Shutdown => write!(f, "shutdown"),
ExitReason::Custom(msg) => write!(f, "{}", msg),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MonitorRef(pub(crate) u64);
impl MonitorRef {
pub(crate) fn new(id: u64) -> Self {
Self(id)
}
}
impl fmt::Display for MonitorRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#Ref<{}>", self.0)
}
}
#[derive(Debug)]
pub(crate) struct Envelope {
pub(crate) content: EnvelopeContent,
#[cfg(feature = "telemetry")]
pub(crate) enqueued_at: std::time::Instant,
#[cfg(feature = "telemetry")]
#[allow(dead_code)] pub(crate) parent_span_id: Option<String>,
}
#[derive(Debug)]
pub(crate) enum EnvelopeContent {
Message(Message),
Signal(Signal),
}
impl Envelope {
pub(crate) fn message(msg: Message) -> Self {
Envelope {
content: EnvelopeContent::Message(msg),
#[cfg(feature = "telemetry")]
enqueued_at: std::time::Instant::now(),
#[cfg(feature = "telemetry")]
parent_span_id: Self::capture_span_id(),
}
}
pub(crate) fn signal(signal: Signal) -> Self {
Envelope {
content: EnvelopeContent::Signal(signal),
#[cfg(feature = "telemetry")]
enqueued_at: std::time::Instant::now(),
#[cfg(feature = "telemetry")]
parent_span_id: Self::capture_span_id(),
}
}
#[cfg(feature = "telemetry")]
fn capture_span_id() -> Option<String> {
let current_span = Span::current();
if current_span.is_disabled() {
None
} else {
Some(format!("{:?}", current_span.id()?))
}
}
#[cfg(feature = "telemetry")]
#[allow(dead_code)] pub(crate) fn parent_span_id(&self) -> Option<&str> {
self.parent_span_id.as_deref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exit_reason_normal() {
let reason = ExitReason::Normal;
assert!(reason.is_normal());
assert!(reason.is_trappable());
}
#[test]
fn test_exit_reason_killed() {
let reason = ExitReason::Killed;
assert!(!reason.is_normal());
assert!(!reason.is_trappable());
}
#[test]
fn test_exit_reason_display() {
assert_eq!(ExitReason::Normal.to_string(), "normal");
assert_eq!(ExitReason::Killed.to_string(), "killed");
assert_eq!(
ExitReason::Panic("error".to_string()).to_string(),
"panic: error"
);
}
#[test]
fn test_monitor_ref() {
let ref1 = MonitorRef::new(1);
let ref2 = MonitorRef::new(2);
assert_ne!(ref1, ref2);
assert_eq!(ref1, MonitorRef::new(1));
}
#[test]
fn test_signal_clone() {
let signal = Signal::Stop;
let cloned = signal.clone();
assert!(matches!(cloned, Signal::Stop));
}
#[test]
fn test_message_boxing() {
let msg: Message = Box::new(42i32);
let value = msg.downcast_ref::<i32>();
assert_eq!(value, Some(&42));
}
}