//! This module contains types used to model the actor system implemented by the runtime. use std::{fmt::Display, sync::Arc}; use btlib::BlockPath; use btserde::field_helpers::smart_ptr; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use uuid::Uuid; /// Represents the result of an actor state transition, which can be one of the following: /// * `Success`: The transition succeeded and the new state is contained in the result. /// * `Abort`: The transition failed and the previous state along with an error describing /// the failure is in the result. /// * `Fatal`: The transition failed and the actor cannot recover from this failure. An error /// describing the failure is contained in the result. pub enum TransResult { /// Represents a successful transition. Ok(To), /// Represents an aborted transition. Abort { from: From, err: btlib::Error }, /// Represents a failed transition which kills the actor which attempted it. Fatal { err: btlib::Error }, } /// Specifies a kind of transition, either a `Send` or a `Receive`. pub enum TransKind { /// A transition which doesn't receive any message, but sends one or more. Send, /// A transition which receives a message. Receive, } impl TransKind { const fn verb(&self) -> &'static str { match self { Self::Send => "sending", Self::Receive => "receiving", } } } /// A struct which conveys information about where an actor panic occurred to the kernel. pub struct ActorPanic { /// The name of the actor implementation which panicked. pub actor_impl: String, /// The name of the state the actor was in. pub state: &'static str, /// The name of the message the actor was handling, or the name of the first message it was /// trying to send. pub message: &'static str, /// The kind of transition which was being attempted. pub kind: TransKind, /// An error describing why the panic occurred. pub err: btlib::Error, } impl Display for ActorPanic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "Actor {} panicked in state {} while {} a message of type {}: {}", self.actor_impl, self.state, self.kind.verb(), self.message, self.err ) } } /// Represents the terminal state of an actor, where it stops processing messages and halts. pub struct End; impl End { /// Returns the identifier for this type which is expected in protocol definitions. pub fn ident() -> &'static str { stringify!(End) } } /// A unique identifier for a particular service. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)] pub struct ServiceId(#[serde(with = "smart_ptr")] Arc); impl ServiceId { pub fn new(value: Arc) -> Self { Self(value) } } impl AsRef for ServiceId { fn as_ref(&self) -> &str { self.0.as_str() } } impl> From for ServiceId { fn from(value: T) -> Self { Self(Arc::new(value.into())) } } impl Display for ServiceId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.as_ref()) } } /// A unique identifier for a service. /// /// A service is a collection of actors in the same directory which provide some functionality. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)] pub struct ServiceName { /// The path to the directory containing the service. #[serde(with = "smart_ptr")] path: Arc, /// The id of the service. service_id: ServiceId, } impl ServiceName { pub fn new(path: Arc, service_id: ServiceId) -> Self { Self { path, service_id } } } /// Indicates the set of service providers a message is addressed to. /// /// The message could be delivered to any of the service providers in this set, at random. pub struct ServiceAddr { /// The [ServiceName] to address the message to. name: ServiceName, #[allow(dead_code)] /// Indicates if the message should be routed towards the root of the tree or away from it. rootward: bool, } impl ServiceAddr { pub fn new(name: ServiceName, rootward: bool) -> Self { Self { name, rootward } } pub fn path(&self) -> &Arc { &self.name.path } pub fn service_id(&self) -> &ServiceId { &self.name.service_id } } /// An identifier for an actor which is unique in a given runtime. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)] pub struct ActorId(Uuid); impl ActorId { pub fn new() -> Self { Self(Uuid::new_v4()) } /// Returns an actor ID that can't have messages delivered to it. pub fn undeliverable() -> Self { Self(Uuid::from_bytes([0; 16])) } } impl Default for ActorId { fn default() -> Self { Self::new() } } impl Copy for ActorId {} impl Display for ActorId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } /// A unique identifier for a specific actor activation. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)] pub struct ActorName { /// The path to the directory containing this actor. #[serde(with = "smart_ptr")] path: Arc, /// A unique identifier for an actor activation. Even as an actor transitions to different types /// as it handles messages, this value does not change. Thus this value can be used to trace an /// actor through a series of state transitions. act_id: ActorId, } impl ActorName { pub fn new(path: Arc, act_id: ActorId) -> Self { Self { path, act_id } } pub fn path(&self) -> &Arc { &self.path } pub fn act_id(&self) -> ActorId { self.act_id } } impl Display for ActorName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}@{}", self.act_id, self.path) } } /// Trait for messages which expect exactly one reply. pub trait CallMsg: Serialize + DeserializeOwned + Send + Sync { /// The reply type expected for this message. type Reply: Serialize + DeserializeOwned + Send + Sync; } /// Trait for messages which expect exactly zero replies. pub trait SendMsg: CallMsg {} /// A type used to express when a reply is not expected for a message type. #[derive(Serialize, Deserialize)] pub enum NoReply {}