diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 0a84c40..23bd0c2 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -8,15 +8,14 @@ To create a `Projector`, you need to provide the target location and the transpo ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -# use chorus_lib::core::{ChoreographyLocation, Projector}; -# use chorus_lib::{LocationSet}; -# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); -# let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; +# use chorus_lib::core::{ChoreographyLocation, Projector, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; +# let transport_channel = LocalTransportChannelBuilder::new().with(Alice).with(Bob).build(); +# let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); let projector = Projector::new(Alice, alice_transport); ``` @@ -28,10 +27,9 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# use chorus_lib::{LocationSet}; -# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; +# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp, LocationSet}; +# let transport_channel = LocalTransportChannelBuilder::new().with(Alice).with(Bob).build(); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] # struct Alice; diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 504030e..aceab44 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -8,46 +8,48 @@ ChoRus provides two built-in transports: `local` and `http`. ### The Local Transport -The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. Each `local` transport is defined over `LocalTransportChannel`, which contains the set of `ChoreographyLocation` that the `local` transport operates on. You can build a `LocalTransportChannel` by importing the `LocalTransportChannel` struct from the `chorus_lib` crate. +The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. + +To use the local transport, we first need to create a `LocalTransportChannel`, which works as a channel between threads and allows them to send messages to each other. To do so, we use the `LocalTransportChannelBuilder` struct from the `chorus_lib` crate. ```rust # extern crate chorus_lib; -# use chorus_lib::core::{ChoreographyLocation}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; -use chorus_lib::transport::local::LocalTransportChannel; +use chorus_lib::transport::local::LocalTransportChannelBuilder; -let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); +let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .build(); ``` -To use the `local` transport, first import the `LocalTransport` struct from the `chorus_lib` crate. - -Then build the transport by using the `LocalTransport::new` associated function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. +Using the `with` method, we add locations to the channel. When we call `build`, it will create an instance of `LocalTransportChannel`. + +Then, create a transport by using the `LocalTransport::new` function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. ```rust # extern crate chorus_lib; -# use chorus_lib::core::{ChoreographyLocation}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; -# use chorus_lib::transport::local::LocalTransportChannel; -# let transport_channel = LocalTransportChannel::new().with(Alice); +# use chorus_lib::transport::local::LocalTransportChannelBuilder; +# let transport_channel = LocalTransportChannelBuilder::new().with(Alice).build(); use chorus_lib::transport::local::{LocalTransport}; let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); ``` -Because of the nature of the `Local` transport, you must use the same `LocalTransportChannel` instance for all locations. You can `clone` the `LocalTransprotChannel` instance and pass it to each `Projector::new` constructor. +Because of the nature of the `Local` transport, you must use the same `LocalTransportChannel` instance for all locations. You can `clone` the `LocalTransportChannel` instance and pass it to each `Projector::new` constructor. ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # use std::thread; -# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -58,7 +60,10 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # fn run(self, op: &impl ChoreoOp) { # } # } -let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); +let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .build(); let mut handles: Vec> = Vec::new(); { // create a transport for Alice @@ -82,21 +87,23 @@ let mut handles: Vec> = Vec::new(); The `http` transport is used to execute choreographies on different machines. This is useful for executing choreographies in a distributed system. -To use the `http` transport, import the `HttpTransport` struct and the `HttpTransportConfig` type alias from the `chorus_lib` crate. +To use the `http` transport, import `HttpTransport` and `HttpTransportConfigBuilder` from the `chorus_lib` crate. ```rust # extern crate chorus_lib; -use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; +use chorus_lib::transport::http::{HttpTransport, HttpTransportConfigBuilder}; ``` -The primary constructor requires an argument of type `HttpTransportConfig`. To create an instance of this configuration, start with `HttpTransportConfig::for_target(target_location, (hostname, port))`. It will create set a projection target and the hostname and port to listen on. Then, provide information to connect to other locations by method-chaining the `.with(other_location, (hostname, port))` method. You can think of `HttpTransportConfig` as a mapping from locations to their hostnames and ports. +We need to construct a `HttpTransportConfig` using the `HttpTransportConfigBuilder`. First, we specify the target location and the hostname and port to listen on using the `for_target` method. Then, we specify the other locations and their `(hostname, port)` pairs using the `with` method. ```rust {{#include ./header.txt}} -# use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; -let config = HttpTransportConfig::for_target(Alice, ("localhost".to_string(), 8080)) - .with(Bob, ("localhost".to_string(), 8081)); - +# use chorus_lib::transport::http::{HttpTransport, HttpTransportConfigBuilder}; +// `Alice` listens on port 8080 on localhost +let config = HttpTransportConfigBuilder::for_target(Alice, ("localhost".to_string(), 8080)) + // Connect to `Bob` on port 8081 on localhost + .with(Bob, ("localhost".to_string(), 8081)) + .build(); let transport = HttpTransport::new(config); ``` @@ -108,24 +115,23 @@ You can also create your own transport by implementing the `Transport` trait. It ```rust {{#include ./header.txt}} -# use chorus_lib::transport::TransportConfig; -let config = TransportConfig::for_target(Alice, ()) +# use chorus_lib::transport::TransportConfigBuilder; +let config = TransportConfigBuilder::for_target(Alice, ()) .with(Bob, ("localhost".to_string(), 8081)) - .with(Carol, ("localhost".to_string(), 8082)); + .with(Carol, ("localhost".to_string(), 8082)) + .build(); ``` See the API documentation for more details. - ### Note on the location set of the Choreography -Note that when calling `epp_and_run` on a `Projector`, you will get a compile error if the location set of the `Choreography` is not a subset of the location set of the `Transport`. In other words, the `Transport` should have information about every `ChoreographyLocation` that `Choreography` can talk about. So this will fail: +Note that when calling `epp_and_run` on a `Projector`, you will get a compile error if the location set of the `Choreography` is not a subset of the location set of the `Transport`. In other words, the `Transport` should have information about every `ChoreographyLocation` that `Choreography` can talk about. So this will fail: ```rust, compile_fail # extern crate chorus_lib; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; +# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; @@ -138,7 +144,7 @@ impl Choreography for HelloWorldChoreography { } } -let transport_channel = LocalTransportChannel::::new(); +let transport_channel = LocalTransportChannelBuilder::new().with(Alice).build(); let transport = LocalTransport::new(Alice, transport_channel.clone()); let projector = Projector::new(Alice, transport); projector.epp_and_run(HelloWorldChoreography); diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 7231c30..f24bfe6 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,14 +1,13 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner, LocationSet}; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # #[derive(ChoreographyLocation)] # struct Carol; -# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob).with(Carol); +# let transport_channel = LocalTransportChannelBuilder::new().with(Alice).with(Bob).with(Carol).build(); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # let bob_transport = LocalTransport::new(Bob, transport_channel.clone()); # let carol_transport = LocalTransport::new(Carol, transport_channel.clone()); \ No newline at end of file diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 7889aa6..2237be3 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -3,11 +3,11 @@ extern crate chorus_lib; use std::io; use std::thread; +use chorus_lib::transport::local::LocalTransportChannelBuilder; use chrono::NaiveDate; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; -use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -use chorus_lib::LocationSet; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; +use chorus_lib::transport::local::LocalTransport; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { match title.trim() { @@ -73,7 +73,10 @@ impl Choreography for BooksellerChoreography { } fn main() { - let transport_channel = LocalTransportChannel::new().with(Seller).with(Buyer); + let transport_channel = LocalTransportChannelBuilder::new() + .with(Seller) + .with(Buyer) + .build(); let transport_seller = LocalTransport::new(Seller, transport_channel.clone()); let transport_buyer = LocalTransport::new(Buyer, transport_channel.clone()); diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 729d5af..b62c0da 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -4,10 +4,9 @@ use std::collections::HashMap; use std::sync::Arc; use std::thread; -use chorus_lib::LocationSet; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, - transport::local::{LocalTransport, LocalTransportChannel}, + core::{ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Projector}, + transport::local::{LocalTransport, LocalTransportChannelBuilder}, }; use chrono::NaiveDate; @@ -143,10 +142,11 @@ fn main() { i }; - let transport_channel = LocalTransportChannel::new() + let transport_channel = LocalTransportChannelBuilder::new() .with(Seller) .with(Buyer1) - .with(Buyer2); + .with(Buyer2) + .build(); let seller_projector = Arc::new(Projector::new( Seller, diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index ba8bcfd..f861ed2 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -2,9 +2,8 @@ extern crate chorus_lib; use std::thread; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; -use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -use chorus_lib::LocationSet; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; // --- Define two locations (Alice and Bob) --- @@ -19,6 +18,9 @@ struct HelloWorldChoreography; // Implement the `Choreography` trait for `HelloWorldChoreography` impl Choreography for HelloWorldChoreography { + // Define the set of locations involved in the choreography. + // In this case, the set consists of `Alice` and `Bob` and + // the choreography can use theses locations. type L = LocationSet!(Alice, Bob); fn run(self, op: &impl ChoreoOp) { // Create a located value at Alice @@ -40,11 +42,12 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a transport channel - // let transport_channel = LocalTransportChannel::::new(); - let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .build(); // Run the choreography in two threads { - // let transport_channel = transport_channel.clone(); let transport = LocalTransport::new(Alice, transport_channel.clone()); handles.push(thread::spawn(move || { let p = Projector::new(Alice, transport); @@ -52,7 +55,6 @@ fn main() { })); } { - // let transport_channel = transport_channel.clone(); let transport = LocalTransport::new(Bob, transport_channel.clone()); handles.push(thread::spawn(move || { let p = Projector::new(Bob, transport); diff --git a/chorus_lib/examples/input-output.rs b/chorus_lib/examples/input-output.rs index a02844b..7664a65 100644 --- a/chorus_lib/examples/input-output.rs +++ b/chorus_lib/examples/input-output.rs @@ -1,8 +1,5 @@ extern crate chorus_lib; -use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located}, - LocationSet, -}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet}; #[derive(ChoreographyLocation)] struct Alice; #[derive(ChoreographyLocation)] diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index e6b8e0b..b12b3af 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -3,10 +3,9 @@ use std::fmt::Debug; use std::thread; use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, + ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Portable, Projector, }; -use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -use chorus_lib::LocationSet; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; #[derive(ChoreographyLocation)] struct Alice; @@ -57,10 +56,11 @@ impl Choreography> for MainChoreography { } fn main() { - let transport_channel = LocalTransportChannel::new() + let transport_channel = LocalTransportChannelBuilder::new() .with(Alice) .with(Bob) - .with(Carol); + .with(Carol) + .build(); let mut handles = vec![]; { diff --git a/chorus_lib/examples/runner.rs b/chorus_lib/examples/runner.rs index 83dd4a7..e6d39ec 100644 --- a/chorus_lib/examples/runner.rs +++ b/chorus_lib/examples/runner.rs @@ -1,7 +1,6 @@ extern crate chorus_lib; -use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Runner, Superposition}, - LocationSet, +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Runner, Superposition, }; #[derive(ChoreographyLocation)] diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 79abb03..3cc4518 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -1,13 +1,12 @@ /// Choreographic tik-tak-toe game extern crate chorus_lib; -use chorus_lib::transport::http::HttpTransportConfig; use chorus_lib::{ core::{ - ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, LocationSet, Projector, + Serialize, }, - transport::http::HttpTransport, - LocationSet, + transport::http::{HttpTransport, HttpTransportConfigBuilder}, }; use clap::Parser; @@ -295,7 +294,7 @@ fn main() { match args.player { 'X' => { - let config = HttpTransportConfig::for_target( + let config = HttpTransportConfigBuilder::for_target( PlayerX, (args.hostname.as_str().to_string(), args.port), ) @@ -305,7 +304,8 @@ fn main() { args.opponent_hostname.as_str().to_string(), args.opponent_port, ), - ); + ) + .build(); let transport = HttpTransport::new(config); let projector = Projector::new(PlayerX, transport); @@ -315,7 +315,7 @@ fn main() { }); } 'O' => { - let config = HttpTransportConfig::for_target( + let config = HttpTransportConfigBuilder::for_target( PlayerO, (args.hostname.as_str().to_string(), args.port), ) @@ -325,7 +325,8 @@ fn main() { args.opponent_hostname.as_str().to_string(), args.opponent_port, ), - ); + ) + .build(); let transport = HttpTransport::new(config); let projector = Projector::new(PlayerO, transport); diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index f2ba43e..426ad39 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -6,15 +6,28 @@ use std::marker::PhantomData; use serde::de::DeserializeOwned; // re-export so that users can use derive macros without importing serde +#[doc(no_inline)] pub use serde::{Deserialize, Serialize}; -/// Represents a location. It can be derived using `#[derive(ChoreographyLocation)]`. +/// Represents a location. +/// +/// It can be derived using `#[derive(ChoreographyLocation)]`. +/// +/// ``` +/// # use chorus_lib::core::ChoreographyLocation; +/// # +/// #[derive(ChoreographyLocation)] +/// struct Alice; +/// ``` pub trait ChoreographyLocation: Copy { /// Returns the name of the location as a string. fn name() -> &'static str; } -/// Represents a value that can be used in a choreography. ChoRus uses [serde](https://serde.rs/) to serialize and deserialize values. +/// Represents a value that can be used in a choreography. +/// +/// ChoRus uses [serde](https://serde.rs/) to serialize and deserialize values. +/// /// It can be derived using `#[derive(Serialize, Deserialize)]` as long as all the fields satisfy the `Portable` trait. pub trait Portable: Serialize + DeserializeOwned {} impl Portable for T {} @@ -92,15 +105,18 @@ where // --- HList and Helpers --- /// heterogeneous list +#[doc(hidden)] pub trait HList { /// returns fn to_string_list() -> Vec<&'static str>; } /// end of HList +#[doc(hidden)] pub struct HNil; /// An element of HList +#[doc(hidden)] pub struct HCons(Head, Tail); impl HList for HNil { @@ -120,24 +136,50 @@ where } } -// TODO(shumbo): Export the macro under the `core` module +// To export `LocationSet` under the `core` module, we define an internal macro and export it. +// This is because Rust does not allow us to export a macro from a module without re-exporting it. +// `__ChoRus_Internal_LocationSet` is the internal macro and it is configured not to be visible in the documentation. -/// Macro to generate hlist +/// Macro to define a set of locations that a choreography is defined on. +/// +/// ``` +/// # use chorus_lib::core::{ChoreographyLocation, LocationSet}; +/// # +/// # #[derive(ChoreographyLocation)] +/// # struct Alice; +/// # #[derive(ChoreographyLocation)] +/// # struct Bob; +/// # #[derive(ChoreographyLocation)] +/// # struct Carol; +/// # +/// type L = LocationSet!(Alice, Bob, Carol); +/// ``` +#[doc(hidden)] #[macro_export] -macro_rules! LocationSet { +macro_rules! __ChoRus_Internal_LocationSet { () => { $crate::core::HNil }; ($head:ty $(,)*) => { $crate::core::HCons<$head, $crate::core::HNil> }; - ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, $crate::LocationSet!($($tail)*)> }; + ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, $crate::core::LocationSet!($($tail)*)> }; } +#[doc(inline)] +pub use __ChoRus_Internal_LocationSet as LocationSet; + /// Marker +#[doc(hidden)] pub struct Here; /// Marker +#[doc(hidden)] pub struct There(Index); -/// Check membership +/// Check if a location is a member of a location set +/// +/// The trait is used to check if a location is a member of a location set. +/// +/// It takes two type parameters `L` and `Index`. `L` is a location set and `Index` is some type that is inferred by the compiler. +/// If a location `L1` is in `L`, then there exists a type `Index` such that `L1` implements `Member`. pub trait Member { - /// Return HList of non-member + /// A location set that is the remainder of `L` after removing the member. type Remainder: HList; } @@ -156,7 +198,12 @@ where type Remainder = HCons; } -/// Check subset +/// Check if a location set is a subset of another location set +/// +/// The trait is used to check if a location set is a subset of another location set. +/// +/// It takes two type parameters `L` and `Index`. `L` is a location set and `Index` is some type that is inferred by the compiler. +/// If a location set `M` is a subset of `L`, then there exists a type `Index` such that `M` implements `Subset`. pub trait Subset {} // Base case: HNil is a subset of any set @@ -266,7 +313,7 @@ pub trait Choreography { /// /// The type parameter `L` is the location set that the transport is operating on. /// -/// The type paramter `TargetLocation` is the target `ChoreographyLocation`. +/// The type parameter `TargetLocation` is the target `ChoreographyLocation`. pub trait Transport { /// Returns a list of locations. fn locations(&self) -> Vec; @@ -295,7 +342,8 @@ where /// /// - `target` is the projection target of the choreography. /// - `transport` is an implementation of `Transport`. - pub fn new(_target: L1, transport: B) -> Self { + pub fn new(target: L1, transport: B) -> Self { + _ = target; Projector { target: PhantomData, transport, diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 9f8209a..1ddfc70 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,51 +3,86 @@ pub mod http; pub mod local; -use crate::core::{ChoreographyLocation, HCons, HList}; -use crate::LocationSet; +use crate::core::{ChoreographyLocation, HCons, HList, LocationSet}; use std::collections::HashMap; use std::marker::PhantomData; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig -{ +pub struct TransportConfig { /// The information about locations - info: HashMap, + pub info: HashMap, /// The information about the target choreography - target_info: (TargetLocation, TargetInfoType), + pub target_info: (Target, TargetInfo), /// The struct is parametrized by the location set (`L`). location_set: PhantomData, } -impl - TransportConfig +/// A builder for `TransportConfig`. +/// +/// Use this builder to create a `TransportConfig` instance. +/// +/// # Examples +/// +/// ``` +/// use chorus_lib::core::{LocationSet, ChoreographyLocation}; +/// use chorus_lib::transport::TransportConfigBuilder; +/// +/// #[derive(ChoreographyLocation)] +/// struct Alice; +/// +/// #[derive(ChoreographyLocation)] +/// struct Bob; +/// +/// let transport_config = TransportConfigBuilder::for_target(Alice, "value_for_target".to_string()) +/// .with(Bob, "value_for_bob".to_string()) +/// .build(); +/// ``` +pub struct TransportConfigBuilder { + target: (Target, TargetInfo), + location_set: PhantomData, + info: HashMap, +} + +impl + TransportConfigBuilder { - /// A transport for a given target. - pub fn for_target(location: TargetLocation, info: TargetInfoType) -> Self { + /// Creates a new `TransportConfigBuilder` instance for a given target. + pub fn for_target(target: Target, info: TargetInfo) -> Self { Self { - info: HashMap::new(), - target_info: (location, info), + target: (target, info), location_set: PhantomData, + info: HashMap::new(), } } } -impl - TransportConfig +impl + TransportConfigBuilder { /// 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( - mut self, - _location: NewLocation, - info: InfoType, - ) -> TransportConfig, InfoType, TargetLocation, TargetInfoType> -where { - self.info.insert(NewLocation::name().to_string(), info); + self, + location: NewLocation, + info: Info, + ) -> TransportConfigBuilder, Info> { + _ = location; + let mut new_info = self.info; + new_info.insert(NewLocation::name().to_string(), info); + TransportConfigBuilder { + target: self.target, + location_set: PhantomData, + info: new_info, + } + } + /// Builds a `TransportConfig` instance. + pub fn build(self) -> TransportConfig { TransportConfig { info: self.info, - target_info: self.target_info, + target_info: self.target, location_set: PhantomData, } } diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 0c20a58..bb019ea 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,16 +12,37 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; -use crate::transport::TransportConfig; - use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, + transport::{TransportConfig, TransportConfigBuilder}, utils::queue::BlockingQueue, }; type QueueMap = HashMap>; -/// A type alias for `TransportConfig`s used for building `HttpTransport` -pub type HttpTransportConfig = TransportConfig; + +/// Config for `HttpTransport`. +pub type HttpTransportConfig = TransportConfig; + +/// A builder for `HttpTransportConfig`. +/// +/// # Examples +/// +/// ``` +/// # use chorus_lib::core::{LocationSet, ChoreographyLocation}; +/// # use chorus_lib::transport::http::HttpTransportConfigBuilder; +/// # +/// # #[derive(ChoreographyLocation)] +/// # struct Alice; +/// # +/// # #[derive(ChoreographyLocation)] +/// # struct Bob; +/// # +/// let transport_config = HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9010)) +/// .with(Bob, ("example.com".to_string(), 80)) +/// .build(); +/// ``` +pub type HttpTransportConfigBuilder = + TransportConfigBuilder; /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; @@ -155,8 +176,10 @@ mod tests { let mut handles = Vec::new(); { - let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9010)) - .with(Bob, ("localhost".to_string(), 9011)); + let config = + HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9010)) + .with(Bob, ("localhost".to_string(), 9011)) + .build(); handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start @@ -165,8 +188,9 @@ mod tests { })); } { - let config = HttpTransportConfig::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".to_string(), 9011)) + .with(Alice, ("localhost".to_string(), 9010)) + .build(); handles.push(thread::spawn(move || { let transport = HttpTransport::new(config); @@ -187,8 +211,10 @@ mod tests { let mut handles = Vec::new(); { - let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9020)) - .with(Bob, ("localhost".to_string(), 9021)); + let config = + HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9020)) + .with(Bob, ("localhost".to_string(), 9021)) + .build(); handles.push(thread::spawn(move || { signal.send(()).unwrap(); @@ -197,8 +223,9 @@ mod tests { })); } { - let config = HttpTransportConfig::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".to_string(), 9021)) + .with(Alice, ("localhost".to_string(), 9020)) + .build(); handles.push(thread::spawn(move || { // wait for Alice to start, which forces Alice to retry diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 3ba58b3..9e7f447 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,9 +7,7 @@ use serde_json; use std::marker::PhantomData; -use crate::LocationSet; - -use crate::core::{ChoreographyLocation, HCons, HList, Portable, Transport}; +use crate::core::{ChoreographyLocation, HCons, HList, LocationSet, Portable, Transport}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; @@ -30,25 +28,29 @@ impl Clone for LocalTransportChannel { } } -impl LocalTransportChannel { - /// Creates a new `LocalTransportChannel` instance - pub fn new() -> Self { - Self { - location_set: PhantomData, - queue_map: HashMap::new().into(), - } - } -} - impl LocalTransportChannel { - /// Adds a new location to the set of locations in the `LocalTransportChannel`. - pub fn with( - self, - _location: NewLocation, - ) -> LocalTransportChannel> { + /// Creates a new `LocalTransportChannel` instance. + /// + /// You must specify the location set of the channel. The channel can only be used by locations in the set. + /// + /// In most cases, you should use `LocalTransportChannelBuilder` instead of calling this method directly. + /// + /// # Examples + /// ``` + /// use chorus_lib::transport::local::{LocalTransportChannel}; + /// use chorus_lib::core::{LocationSet, ChoreographyLocation}; + /// + /// #[derive(ChoreographyLocation)] + /// struct Alice; + /// + /// #[derive(ChoreographyLocation)] + /// struct Bob; + /// + /// let transport_channel = LocalTransportChannel::::new(); + /// ``` + pub fn new() -> LocalTransportChannel { let mut queue_map: QueueMap = HashMap::new(); - let mut str_list = L::to_string_list(); - str_list.push(NewLocation::name()); + let str_list = L::to_string_list(); for sender in &str_list { let mut n = HashMap::new(); for receiver in &str_list { @@ -64,11 +66,63 @@ impl LocalTransportChannel { } } +/// A builder for `LocalTransportChannel`. +/// +/// Use this builder to create a `LocalTransportChannel` instance. +/// +/// # Examples +/// +/// ``` +/// use chorus_lib::core::{LocationSet, ChoreographyLocation}; +/// use chorus_lib::transport::local::{LocalTransportChannelBuilder}; +/// +/// #[derive(ChoreographyLocation)] +/// struct Alice; +/// +/// #[derive(ChoreographyLocation)] +/// struct Bob; +/// +/// let transport_channel = LocalTransportChannelBuilder::new() +/// .with(Alice) +/// .with(Bob) +/// .build(); +/// ``` +pub struct LocalTransportChannelBuilder { + location_set: PhantomData, +} + +impl LocalTransportChannelBuilder { + /// Creates a new `LocalTransportChannelBuilder` instance + pub fn new() -> Self { + Self { + location_set: PhantomData, + } + } +} + +impl LocalTransportChannelBuilder { + /// Adds a new location to the set of locations in the `LocalTransportChannel`. + pub fn with( + &self, + location: NewLocation, + ) -> LocalTransportChannelBuilder> { + _ = location; + LocalTransportChannelBuilder { + location_set: PhantomData, + } + } + + /// Builds a `LocalTransportChannel` instance. + pub fn build(&self) -> LocalTransportChannel { + LocalTransportChannel::new() + } +} + /// The local transport. /// /// This transport uses a blocking queue to allow for communication between threads. Each location must be executed in its thread. /// -/// Unlike network-based transports, all locations must share the same `LocalTransport` instance. The struct implements `Clone` so that it can be shared across threads. +/// 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, location_set: PhantomData, @@ -78,7 +132,9 @@ pub struct LocalTransport { impl LocalTransport { /// Creates a new `LocalTransport` instance from a Target `ChoreographyLocation` and a `LocalTransportChannel`. - pub fn new(_target: TargetLocation, local_channel: LocalTransportChannel) -> Self { + pub fn new(target: TargetLocation, local_channel: LocalTransportChannel) -> Self { + _ = target; + let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); @@ -142,7 +198,10 @@ mod tests { fn test_local_transport() { let v = 42; - let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .build(); let mut handles = Vec::new(); {