use crate::node::ActorId;
use crate::remote::WireEnvelope;
use crate::system_actors;
use async_trait::async_trait;
#[derive(Debug, Clone)]
pub struct RoutingError {
pub message: String,
}
impl RoutingError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
impl std::fmt::Display for RoutingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "routing error: {}", self.message)
}
}
impl std::error::Error for RoutingError {}
#[derive(Debug, Clone)]
pub enum RoutingOutcome {
SpawnCompleted {
request_id: String,
actor_id: ActorId,
},
SpawnFailed {
request_id: String,
error: String,
},
Acknowledged,
CancelAcknowledged,
CancelNotFound {
reason: String,
},
}
#[async_trait]
pub trait SystemMessageRouter: Send + Sync {
async fn route_system_envelope(
&self,
envelope: WireEnvelope,
) -> Result<RoutingOutcome, RoutingError>;
}
pub fn validate_system_message_type(message_type: &str) -> Result<(), RoutingError> {
if system_actors::is_system_message_type(message_type) {
Ok(())
} else {
Err(RoutingError::new(format!(
"unknown system message type: {message_type}"
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::system_actors::*;
#[test]
fn is_system_message_type_recognizes_all_types() {
assert!(is_system_message_type(SYSTEM_MSG_TYPE_SPAWN));
assert!(is_system_message_type(SYSTEM_MSG_TYPE_WATCH));
assert!(is_system_message_type(SYSTEM_MSG_TYPE_UNWATCH));
assert!(is_system_message_type(SYSTEM_MSG_TYPE_CANCEL));
assert!(is_system_message_type(SYSTEM_MSG_TYPE_CONNECT_PEER));
assert!(is_system_message_type(SYSTEM_MSG_TYPE_DISCONNECT_PEER));
}
#[test]
fn is_system_message_type_rejects_unknown() {
assert!(!is_system_message_type("myapp::Counter"));
assert!(!is_system_message_type(""));
assert!(!is_system_message_type("dactor::system_actors::Unknown"));
}
#[test]
fn validate_system_message_type_ok_for_known() {
assert!(validate_system_message_type(SYSTEM_MSG_TYPE_SPAWN).is_ok());
assert!(validate_system_message_type(SYSTEM_MSG_TYPE_CANCEL).is_ok());
}
#[test]
fn validate_system_message_type_err_for_unknown() {
let err = validate_system_message_type("unknown::Type").unwrap_err();
assert!(err.message.contains("unknown system message type"));
}
#[test]
fn routing_error_display() {
let err = RoutingError::new("system actors not started");
assert_eq!(format!("{err}"), "routing error: system actors not started");
}
#[test]
fn routing_outcome_variants() {
let spawn_ok = RoutingOutcome::SpawnCompleted {
request_id: "r1".into(),
actor_id: crate::node::ActorId {
node: crate::node::NodeId("n1".into()),
local: 42,
},
};
assert!(matches!(spawn_ok, RoutingOutcome::SpawnCompleted { .. }));
let spawn_fail = RoutingOutcome::SpawnFailed {
request_id: "r1".into(),
error: "type not found".into(),
};
assert!(matches!(spawn_fail, RoutingOutcome::SpawnFailed { .. }));
assert!(matches!(RoutingOutcome::Acknowledged, RoutingOutcome::Acknowledged));
assert!(matches!(RoutingOutcome::CancelAcknowledged, RoutingOutcome::CancelAcknowledged));
let not_found = RoutingOutcome::CancelNotFound {
reason: "no such request".into(),
};
assert!(matches!(not_found, RoutingOutcome::CancelNotFound { .. }));
}
}