diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 40879dd..363ede8 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -100,9 +100,9 @@ We need to construct a `HttpTransportConfig` using the `HttpTransportConfigBuild {{#include ./header.txt}} # use chorus_lib::transport::http::{HttpTransport, HttpTransportConfigBuilder}; // `Alice` listens on port 8080 on localhost -let config = HttpTransportConfigBuilder::for_target(Alice, ("localhost".to_string(), 8080)) +let config = HttpTransportConfigBuilder::for_target(Alice, ("localhost", 8080)) // Connect to `Bob` on port 8081 on localhost - .with(Bob, ("localhost".to_string(), 8081)) + .with(Bob, ("localhost", 8081)) .build(); let transport = HttpTransport::new(config); ``` @@ -111,14 +111,14 @@ In the above example, the transport will start the HTTP server on port 8080 on l ## Creating a Custom Transport -You can also create your own transport by implementing the `Transport` trait. It might be helpful to first build a `TransportConfig` to have the the information that you need for each `ChoreographyLocation`, and then have a constructor that takes the `TransportConfig` and builds the `Transport` based on it. While the syntax is similar to `HttpTransportConfig`, which is `HttpTransportConfigBuilder::for_target(target_location, target_information)`, chained with information about other locations using the `.with(other_location, other_location_information)`, the type of information for each `ChoreographyLocation` might diverge from the `(host_name, port)` format presented in `HttpTransport`. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. +You can also create your own transport by implementing the `Transport` trait. It might be helpful to first build a `TransportConfig` to have the the information that you need for each `ChoreographyLocation`, and then have a constructor that takes the `TransportConfig` and builds the `Transport` based on it. While the syntax is similar to `HttpTransportConfig`, which is `HttpTransportConfigBuilder::for_target(target_location, target_information)`, chained with information about other locations using the `.with(other_location, other_location_information)`, the type of information for each `ChoreographyLocation` might diverge from the `(host_name, port)` format presented in `HttpTransport`. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. ```rust {{#include ./header.txt}} # use chorus_lib::transport::TransportConfigBuilder; let config = TransportConfigBuilder::for_target(Alice, ()) - .with(Bob, ("localhost".to_string(), 8081)) - .with(Carol, ("localhost".to_string(), 8082)) + .with(Bob, ("localhost", 8081)) + .with(Carol, ("localhost", 8082)) .build(); ``` diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 3cc4518..04f8655 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -296,14 +296,11 @@ fn main() { 'X' => { let config = HttpTransportConfigBuilder::for_target( PlayerX, - (args.hostname.as_str().to_string(), args.port), + (args.hostname.as_str(), args.port), ) .with( PlayerO, - ( - args.opponent_hostname.as_str().to_string(), - args.opponent_port, - ), + (args.opponent_hostname.as_str(), args.opponent_port), ) .build(); @@ -317,14 +314,11 @@ fn main() { 'O' => { let config = HttpTransportConfigBuilder::for_target( PlayerO, - (args.hostname.as_str().to_string(), args.port), + (args.hostname.as_str(), args.port), ) .with( PlayerX, - ( - args.opponent_hostname.as_str().to_string(), - args.opponent_port, - ), + (args.opponent_hostname.as_str(), args.opponent_port), ) .build(); diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 8ab9a2c..ed2d853 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -317,7 +317,7 @@ pub trait Choreography { /// The type parameter `TargetLocation` is the target `ChoreographyLocation`. pub trait Transport { /// Returns a list of locations. - fn locations(&self) -> Vec; + fn locations(&self) -> Vec<&'static str>; /// Sends a message from `from` to `to`. fn send(&self, from: &str, to: &str, data: &V) -> (); /// Receives a message from `from` to `at`. @@ -396,7 +396,7 @@ where > { target: PhantomData, transport: &'a B, - locations: Vec, + locations: Vec<&'static str>, marker: PhantomData, projector_location_set: PhantomData, } @@ -478,15 +478,14 @@ where &self, choreo: C, ) -> R { - let locs_vec = - Vec::from_iter(S::to_string_list().into_iter().map(|s| s.to_string())); + let locs_vec = S::to_string_list(); for location in &locs_vec { if *location == T::name().to_string() { let op = EppOp { target: PhantomData::, transport: self.transport, - locations: locs_vec.clone(), + locations: locs_vec, marker: PhantomData::, projector_location_set: PhantomData::, }; diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 418e45f..090cc1a 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -9,13 +9,14 @@ use std::marker::PhantomData; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig { +pub struct TransportConfig<'a, Target: ChoreographyLocation, TargetInfo, L: LocationSet, Info> { /// The information about locations - pub info: HashMap, + pub info: HashMap<&'static str, Info>, /// The information about the target choreography pub target_info: (Target, TargetInfo), /// The struct is parametrized by the location set (`L`). location_set: PhantomData, + lifetime: PhantomData<&'a ()>, } /// A builder for `TransportConfig`. @@ -38,14 +39,21 @@ pub struct TransportConfig { +pub struct TransportConfigBuilder< + 'a, + Target: ChoreographyLocation, + TargetInfo, + L: LocationSet, + Info, +> { target: (Target, TargetInfo), location_set: PhantomData, - info: HashMap, + info: HashMap<&'static str, Info>, + lifetime: PhantomData<&'a ()>, } -impl - TransportConfigBuilder +impl<'a, Target: ChoreographyLocation, TargetInfo, Info> + TransportConfigBuilder<'a, Target, TargetInfo, LocationSet!(Target), Info> { /// Creates a new `TransportConfigBuilder` instance for a given target. pub fn for_target(target: Target, info: TargetInfo) -> Self { @@ -53,37 +61,40 @@ impl target: (target, info), location_set: PhantomData, info: HashMap::new(), + lifetime: PhantomData, } } } -impl - TransportConfigBuilder +impl<'a, Target: ChoreographyLocation, TargetInfo, L: LocationSet, Info> + TransportConfigBuilder<'a, Target, TargetInfo, L, Info> { /// Adds information about a new `ChoreographyLocation`. /// /// This method tells the builder that the choreography involves a new location and how to communicate with it. - pub fn with( + pub fn with<'b, NewLocation: ChoreographyLocation>( self, location: NewLocation, info: Info, - ) -> TransportConfigBuilder, Info> { + ) -> TransportConfigBuilder<'b, Target, TargetInfo, HCons, Info> { _ = location; let mut new_info = self.info; - new_info.insert(NewLocation::name().to_string(), info); + new_info.insert(NewLocation::name(), info); TransportConfigBuilder { target: self.target, location_set: PhantomData, info: new_info, + lifetime: PhantomData, } } /// Builds a `TransportConfig` instance. - pub fn build(self) -> TransportConfig { + pub fn build<'b>(self) -> TransportConfig<'b, Target, TargetInfo, L, Info> { TransportConfig { info: self.info, target_info: self.target, location_set: PhantomData, + lifetime: PhantomData, } } } diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 4e6024d..8427758 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -18,10 +18,11 @@ use crate::{ utils::queue::BlockingQueue, }; -type QueueMap = HashMap>; +type QueueMap = HashMap<&'static str, BlockingQueue>; /// Config for `HttpTransport`. -pub type HttpTransportConfig = TransportConfig; +pub type HttpTransportConfig<'a, L, Target> = + TransportConfig<'a, Target, (&'a str, u16), L, (&'a str, u16)>; /// A builder for `HttpTransportConfig`. /// @@ -37,19 +38,19 @@ pub type HttpTransportConfig = TransportConfig = - TransportConfigBuilder; +pub type HttpTransportConfigBuilder<'a, Target, L> = + TransportConfigBuilder<'a, Target, (&'a str, u16), L, (&'a str, u16)>; /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; /// The HTTP transport. -pub struct HttpTransport { - config: HashMap, +pub struct HttpTransport<'a, L: LocationSet, TLocation> { + config: HashMap<&'static str, (&'a str, u16)>, agent: Agent, server: Arc, join_handle: Option>, @@ -58,16 +59,16 @@ pub struct HttpTransport { target_location: PhantomData, } -impl HttpTransport { +impl<'a, L: LocationSet, TLocation: ChoreographyLocation> HttpTransport<'a, L, TLocation> { /// Creates a new `HttpTransport` instance from the configuration. - pub fn new(http_config: HttpTransportConfig) -> Self + pub fn new(http_config: HttpTransportConfig<'a, L, TLocation>) -> Self where TLocation: Member, { let queue_map: Arc = { let mut m = HashMap::new(); for loc in L::to_string_list() { - m.insert(loc.to_string(), BlockingQueue::new()); + m.insert(loc, BlockingQueue::new()); } Arc::new(m.into()) }; @@ -119,18 +120,18 @@ impl HttpTransport Drop for HttpTransport { +impl<'a, L: LocationSet, TLocation> Drop for HttpTransport<'a, L, TLocation> { fn drop(&mut self) { self.server.unblock(); self.join_handle.take().map(thread::JoinHandle::join); } } -impl Transport - for HttpTransport +impl<'a, L: LocationSet, TLocation: ChoreographyLocation> Transport + for HttpTransport<'a, L, TLocation> { - fn locations(&self) -> Vec { - Vec::from_iter(self.config.keys().map(|s| s.clone())) + fn locations(&self) -> Vec<&'static str> { + self.config.keys().cloned().collect() } fn send(&self, from: &str, to: &str, data: &V) -> () { @@ -176,10 +177,9 @@ mod tests { let mut handles = Vec::new(); { - let config = - HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9010)) - .with(Bob, ("localhost".to_string(), 9011)) - .build(); + let config = HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0", 9010)) + .with(Bob, ("localhost", 9011)) + .build(); handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start @@ -188,8 +188,8 @@ mod tests { })); } { - let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0".to_string(), 9011)) - .with(Alice, ("localhost".to_string(), 9010)) + let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0", 9011)) + .with(Alice, ("localhost", 9010)) .build(); handles.push(thread::spawn(move || { @@ -211,10 +211,9 @@ mod tests { let mut handles = Vec::new(); { - let config = - HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9020)) - .with(Bob, ("localhost".to_string(), 9021)) - .build(); + let config = HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0", 9020)) + .with(Bob, ("localhost", 9021)) + .build(); handles.push(thread::spawn(move || { signal.send(()).unwrap(); @@ -223,8 +222,8 @@ mod tests { })); } { - let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0".to_string(), 9021)) - .with(Alice, ("localhost".to_string(), 9020)) + let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0", 9021)) + .with(Alice, ("localhost", 9020)) .build(); handles.push(thread::spawn(move || { diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 9419f53..22be7d1 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -124,7 +124,7 @@ impl LocalTransportChannelBuilder { /// /// All locations must share the same `LocalTransportChannel` instance. `LocalTransportChannel` implements `Clone` so that it can be shared across threads. pub struct LocalTransport { - internal_locations: Vec, + internal_locations: Vec<&'static str>, location_set: PhantomData, local_channel: LocalTransportChannel, target_location: PhantomData, @@ -135,15 +135,8 @@ impl LocalTransport { pub fn new(target: TargetLocation, local_channel: LocalTransportChannel) -> Self { _ = target; - let locations_list = L::to_string_list(); - - let mut locations_vec = Vec::new(); - for loc in locations_list.clone() { - locations_vec.push(loc.to_string()); - } - LocalTransport { - internal_locations: locations_vec, + internal_locations: L::to_string_list(), location_set: PhantomData, local_channel, target_location: PhantomData, @@ -154,7 +147,7 @@ impl LocalTransport { impl Transport for LocalTransport { - fn locations(&self) -> Vec { + fn locations(&self) -> Vec<&'static str> { return self.internal_locations.clone(); }