diff --git a/client/src/context.rs b/client/src/context.rs index b8418ae..7505465 100644 --- a/client/src/context.rs +++ b/client/src/context.rs @@ -3,7 +3,7 @@ use parking_lot::Mutex; use solstice_proto::ServerRequest; -use tokio::sync::mpsc::Sender; +use tokio::sync::mpsc::{channel, Receiver, Sender}; use crate::room::RoomMap; use crate::user::UserMap; @@ -18,13 +18,66 @@ pub struct State { /// Holds process-wide context for message handlers to execute against. #[derive(Debug)] pub struct Context { + /// Mutable state. pub state: Mutex, + + /// Sender half of a channel used to send requests to the server. pub server_request_tx: Sender, } +/// Convenience bundle for creating new `Context` structs. +#[derive(Debug)] +pub struct ContextBundle { + /// The context itself. + pub context: Context, + + /// The receiver corresponding to `context.server_request_tx`. + pub server_request_rx: Receiver, +} + +/// Specifies options for new `ContextBundle` structs. +#[derive(Debug)] +pub struct ContextOptions { + /// The state to start out with. + pub initial_state: State, + + /// The buffer size of the server request channel. + pub server_request_buffer: usize, +} + +impl Default for ContextOptions { + fn default() -> Self { + Self { + server_request_buffer: 100, + initial_state: State::default(), + } + } +} + +impl ContextBundle { + /// Builds a new context bundle as configured by `options`. + fn new(options: ContextOptions) -> Self { + let (server_request_tx, server_request_rx) = + channel(options.server_request_buffer); + Self { + context: Context { + state: Mutex::new(options.initial_state), + server_request_tx, + }, + server_request_rx, + } + } +} + +impl Default for ContextBundle { + fn default() -> Self { + Self::new(ContextOptions::default()) + } +} + #[cfg(test)] mod tests { - use super::{Context, State}; + use super::{Context, ContextBundle, State}; #[test] fn default_state_is_empty() { @@ -40,4 +93,12 @@ mod tests { let _sync: &dyn Sync = &context; } } + + #[test] + fn default_bundle_state_is_empty() { + let bundle = ContextBundle::default(); + let guard = bundle.context.state.lock(); + assert_eq!(guard.rooms.get_room_list(), vec![]); + assert_eq!(guard.users.get_list(), vec![]); + } } diff --git a/client/src/executor.rs b/client/src/executor.rs index e3f4982..e5e1c32 100644 --- a/client/src/executor.rs +++ b/client/src/executor.rs @@ -65,17 +65,16 @@ mod tests { use solstice_proto::{User, UserStatus}; use tokio::sync::mpsc::channel; + use crate::context::{Context, ContextBundle}; + use super::{Executor, Job}; - use crate::context::{Context, State}; #[test] fn immediate_join_returns_unchanged_context() { - let (tx, _rx) = channel(1); - let context = Executor::new(Context { - state: Mutex::new(State::default()), - server_request_tx: tx, - }) - .join(); + let bundle = ContextBundle::default(); + + let context = Executor::new(bundle.context).join(); + assert_eq!(context.state.lock().users.get_list(), vec![]); assert_eq!(context.state.lock().rooms.get_room_list(), vec![]); } @@ -92,11 +91,8 @@ mod tests { #[test] fn join_waits_for_all_jobs() { - let (tx, _rx) = channel(1); - let executor = Executor::new(Context { - state: Mutex::new(State::default()), - server_request_tx: tx, - }); + let bundle = ContextBundle::default(); + let executor = Executor::new(bundle.context); let barrier = Arc::new(Barrier::new(2)); @@ -122,11 +118,9 @@ mod tests { #[test] fn jobs_access_context() { - let (tx, _rx) = channel(1); - let executor = Executor::new(Context { - state: Mutex::new(State::default()), - server_request_tx: tx, - }); + let bundle = ContextBundle::default(); + + let executor = Executor::new(bundle.context); let user1 = User { name: "potato".to_string(), diff --git a/client/src/handlers/set_privileged_users_handler.rs b/client/src/handlers/set_privileged_users_handler.rs index 189e725..329b23a 100644 --- a/client/src/handlers/set_privileged_users_handler.rs +++ b/client/src/handlers/set_privileged_users_handler.rs @@ -26,22 +26,16 @@ impl MessageHandler for SetPrivilegedUsersHandler { #[cfg(test)] mod tests { - use parking_lot::Mutex; use solstice_proto::server::PrivilegedUsersResponse; - use tokio::sync::mpsc::channel; - use crate::context::{Context, State}; + use crate::context::ContextBundle; use crate::message_handler::MessageHandler; use super::SetPrivilegedUsersHandler; #[test] fn run_sets_privileged_users() { - let (tx, _rx) = channel(1); - let context = Context { - state: Mutex::new(State::default()), - server_request_tx: tx, - }; + let bundle = ContextBundle::default(); let response = PrivilegedUsersResponse { users: vec![ @@ -52,10 +46,10 @@ mod tests { }; SetPrivilegedUsersHandler::default() - .run(&context, &response) + .run(&bundle.context, &response) .unwrap(); - let mut privileged = context.state.lock().users.get_all_privileged(); + let mut privileged = bundle.context.state.lock().users.get_all_privileged(); privileged.sort(); assert_eq!(privileged, response.users); diff --git a/client/src/main.rs b/client/src/main.rs index 030dfd3..d91e938 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -24,7 +24,7 @@ mod message_handler; mod room; mod user; -use context::{Context, State}; +use context::ContextBundle; use dispatcher::Dispatcher; use executor::Executor; @@ -116,17 +116,16 @@ async fn run_client( } async fn async_main() { - let (server_request_tx, server_request_rx) = tokio::sync::mpsc::channel(100); + let bundle = ContextBundle::default(); + let (dispatcher_tx, mut dispatcher_rx) = tokio::sync::mpsc::unbounded_channel(); - let client_task = tokio::spawn(run_client(server_request_rx, dispatcher_tx)); + let client_task = + tokio::spawn(run_client(bundle.server_request_rx, dispatcher_tx)); let dispatcher = Dispatcher::new(); - let executor = Executor::new(Context { - state: Mutex::new(State::default()), - server_request_tx, - }); + let executor = Executor::new(bundle.context); while let Some(message) = dispatcher_rx.recv().await { if let Some(job) = dispatcher.dispatch(message) {