diff --git a/build.rs b/build.rs index 3950335..12be6c0 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,9 @@ use std::io::Result; fn main() -> Result<()> { - prost_build::compile_protos(&["src/message/wrappers.proto"], &["src/"])?; + prost_build::compile_protos( + &["src/message/wrappers.proto", "src/actor/address.proto"], + &["src/"], + )?; Ok(()) } diff --git a/src/actor/address.proto b/src/actor/address.proto new file mode 100644 index 0000000..9abc34e --- /dev/null +++ b/src/actor/address.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package actor.proto; + +enum Scheme { + LOCAL = 0; + + // Remote is currently a placeholder for future use. + // See busan::actor::address::UriScheme + REMOTE = 1; +} + +// ActorAddress is the serializable representation of an actor address. It can be used +// to send an address to another actor for the purpose of discovery and message routing. +message ActorAddress { + Scheme scheme = 1; + string path = 2; +} + +// AddressList is a simple container for holding a list of ActorAddress messages. +// This allows for multiple addresses to be sent as a single message from/to an actor. +message AddressList { + repeated ActorAddress addresses = 1; +} \ No newline at end of file diff --git a/src/actor/address.rs b/src/actor/address.rs index f1e8be0..57d4a4b 100644 --- a/src/actor/address.rs +++ b/src/actor/address.rs @@ -1,3 +1,4 @@ +use crate::actor::proto::Scheme; use crate::actor::{Letter, Mailbox}; use crate::message::Message; use log::trace; @@ -45,7 +46,7 @@ impl ActorAddress { pub(crate) fn new_root(name: &str) -> Self { Self { - uri: Uri::new(UriScheme::Local, &[name]), + uri: Uri::new(Scheme::Local, &[name]), mailbox: RefCell::new(None), } } @@ -80,18 +81,6 @@ impl ActorAddress { } } -/// `UriScheme` is the transport mechanism for messages sent between actor systems. Messages -/// that stay within the current actor system will all have a `Local` scheme. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub(crate) enum UriScheme { - /// `Local` is the default scheme for messages that stay within the current actor system. - Local, - - /// `Remote` is currently a placeholder value since remote is not currently implemented. - #[allow(dead_code)] - Remote, -} - /// `Uri` is a URI-like type that identifies an actor system and an actor within that system. /// The hierarchical nature, or tree-like, organization of actors is also present in URIs, with /// children and parents readily identifiable by path. Take for example the following hierarchy @@ -112,12 +101,12 @@ pub(crate) enum UriScheme { /// ``` #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Uri { - scheme: UriScheme, - path_segments: Vec, + pub(crate) scheme: Scheme, + pub(crate) path_segments: Vec, } impl Uri { - fn new(scheme: UriScheme, path_segments: &[&str]) -> Self { + fn new(scheme: Scheme, path_segments: &[&str]) -> Self { if path_segments.is_empty() { panic!("Uri must have at least one path segment"); } @@ -160,13 +149,18 @@ impl Uri { fn is_parent(&self, maybe_parent: &Self) -> bool { maybe_parent.is_child(self) } + + /// Return the path component of the [`Uri`] as a string. + pub(crate) fn path(&self) -> String { + self.path_segments.join("/") + } } impl Display for Uri { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self.scheme { - UriScheme::Local => write!(f, "local://")?, - UriScheme::Remote => write!(f, "remote://")?, + Scheme::Local => write!(f, "local://")?, + Scheme::Remote => write!(f, "remote://")?, } write!(f, "{}", self.path_segments.join("/")) } @@ -180,13 +174,13 @@ mod tests { #[should_panic] fn test_construction() { // Test that an empty path is not allowed - Uri::new(UriScheme::Local, &[]); + Uri::new(Scheme::Local, &[]); } #[test] fn test_child_construction() { // Create a child from a root path - let root = Uri::new(UriScheme::Local, &["root"]); + let root = Uri::new(Scheme::Local, &["root"]); let child = root.new_child("child"); // Test the relationships between the two @@ -207,7 +201,7 @@ mod tests { #[test] fn test_self_reference() { - let path = Uri::new(UriScheme::Local, &["root", "some", "path"]); + let path = Uri::new(Scheme::Local, &["root", "some", "path"]); assert_eq!(path.is_child(&path), false); assert_eq!(path.is_parent(&path), false); } @@ -258,7 +252,7 @@ mod tests { ), ]; for (path_segments, expected) in test_cases { - let uri = Uri::new(UriScheme::Local, &path_segments); + let uri = Uri::new(Scheme::Local, &path_segments); assert_eq!(uri.to_string(), expected); } } diff --git a/src/actor/mod.rs b/src/actor/mod.rs index a75c4f5..2679d2f 100644 --- a/src/actor/mod.rs +++ b/src/actor/mod.rs @@ -90,6 +90,8 @@ pub mod address; #[doc(hidden)] pub mod letter; +pub mod proto; + #[doc(inline)] pub use actor::*; #[doc(inline)] diff --git a/src/actor/proto.rs b/src/actor/proto.rs new file mode 100644 index 0000000..7647b70 --- /dev/null +++ b/src/actor/proto.rs @@ -0,0 +1,54 @@ +//! Serializable message types for the actor module +//! +//! This module contains the protobuf definitions that map to internal types +//! such as [`ActorAddress`](crate::actor::ActorAddress) as well as the associated +//! [`ToMessage`](crate::message::ToMessage) implementations for these types. + +use crate::actor; +use crate::message::common_types::impl_busan_message; +use crate::message::{Message, ToMessage}; +use std::cell::RefCell; + +// Import the generated protobuf definitions (see build.rs) +include!(concat!(env!("OUT_DIR"), "/actor.proto.rs")); + +impl_busan_message!(ActorAddress); +impl_busan_message!(AddressList); + +impl ToMessage for &actor::ActorAddress { + fn to_message(self) -> ActorAddress { + ActorAddress { + scheme: match self.uri.scheme { + Scheme::Local => Scheme::Local as i32, + Scheme::Remote => Scheme::Remote as i32, + }, + path: self.uri.path(), + } + } +} + +impl TryFrom for actor::ActorAddress { + type Error = String; + + fn try_from(address: ActorAddress) -> Result { + let scheme = Scheme::from_i32(address.scheme) + .ok_or(format!("Invalid scheme: {}", address.scheme))?; + Ok(actor::ActorAddress { + // TODO: This needs an internal constructor. Presumably there is at least + // one other place we're doing this same thing + uri: actor::Uri { + scheme, + path_segments: address.path.split('/').map(|s| s.to_string()).collect(), + }, + mailbox: RefCell::new(None), + }) + } +} + +impl ToMessage for &[actor::ActorAddress] { + fn to_message(self) -> AddressList { + AddressList { + addresses: self.iter().map(|a| a.to_message()).collect(), + } + } +} diff --git a/src/message/common_types.rs b/src/message/common_types.rs index 37a5bdd..40c5eda 100644 --- a/src/message/common_types.rs +++ b/src/message/common_types.rs @@ -118,6 +118,7 @@ macro_rules! impl_busan_message { } }; } +pub(crate) use impl_busan_message; impl_busan_message!(U32Wrapper); impl_busan_message!(U64Wrapper);