diff --git a/client/src/context.rs b/client/src/context.rs index 68ce50a..b8418ae 100644 --- a/client/src/context.rs +++ b/client/src/context.rs @@ -2,45 +2,42 @@ //! different bits of client state. use parking_lot::Mutex; +use solstice_proto::ServerRequest; +use tokio::sync::mpsc::Sender; -use crate::login::LoginStatus; use crate::room::RoomMap; use crate::user::UserMap; /// Contains all the different bits of client state. -/// -/// Implements `Sync`. -#[derive(Debug)] -pub struct Context { - pub login: Mutex, - pub rooms: Mutex, - pub users: Mutex, +#[derive(Debug, Default)] +pub struct State { + pub rooms: RoomMap, + pub users: UserMap, } -impl Context { - /// Creates a new empty context. - pub fn new() -> Self { - Self { - login: Mutex::new(LoginStatus::Todo), - rooms: Mutex::new(RoomMap::new()), - users: Mutex::new(UserMap::new()), - } - } +/// Holds process-wide context for message handlers to execute against. +#[derive(Debug)] +pub struct Context { + pub state: Mutex, + pub server_request_tx: Sender, } #[cfg(test)] mod tests { - use super::Context; + use super::{Context, State}; #[test] - fn new_context_is_empty() { - let context = Context::new(); - assert_eq!(context.rooms.lock().get_room_list(), vec![]); - assert_eq!(context.users.lock().get_list(), vec![]); + fn default_state_is_empty() { + let state = State::default(); + assert_eq!(state.rooms.get_room_list(), vec![]); + assert_eq!(state.users.get_list(), vec![]); } #[test] fn context_is_sync() { - let _sync: &dyn Sync = &Context::new(); + let option: Option = None; + if let Some(context) = option { + let _sync: &dyn Sync = &context; + } } } diff --git a/client/src/dispatcher.rs b/client/src/dispatcher.rs index b8ad9de..5c34f29 100644 --- a/client/src/dispatcher.rs +++ b/client/src/dispatcher.rs @@ -7,7 +7,7 @@ use solstice_proto::server::ServerResponse; use crate::context::Context; use crate::executor::Job; -use crate::handlers::{LoginHandler, SetPrivilegedUsersHandler}; +use crate::handlers::SetPrivilegedUsersHandler; use crate::message_handler::MessageHandler; /// The type of messages dispatched by a dispatcher. @@ -58,12 +58,6 @@ impl Dispatcher { /// Dispatches the given message by wrapping it with a handler. pub fn dispatch(&self, message: Message) -> Option> { match message { - Message::ServerResponse(ServerResponse::LoginResponse(response)) => { - Some(Box::new(DispatchedMessage { - message: response, - handler: LoginHandler::default(), - })) - } Message::ServerResponse(ServerResponse::PrivilegedUsersResponse( response, )) => Some(Box::new(DispatchedMessage { @@ -98,11 +92,11 @@ mod tests { } #[test] - fn dispatcher_login_response() { + fn dispatcher_unhandled_response() { assert!(Dispatcher::new() .dispatch(into_message(server::LoginResponse::LoginFail { reason: "bleep bloop".to_string(), },)) - .is_some()); + .is_none()); } } diff --git a/client/src/executor.rs b/client/src/executor.rs index 5cde206..e3f4982 100644 --- a/client/src/executor.rs +++ b/client/src/executor.rs @@ -61,15 +61,23 @@ impl Executor { mod tests { use std::sync::{Arc, Barrier}; + use parking_lot::Mutex; use solstice_proto::{User, UserStatus}; + use tokio::sync::mpsc::channel; - use super::{Context, Executor, Job}; + use super::{Executor, Job}; + use crate::context::{Context, State}; #[test] - fn immediate_join_returns_empty_context() { - let context = Executor::new(Context::new()).join(); - assert_eq!(context.users.lock().get_list(), vec![]); - assert_eq!(context.rooms.lock().get_room_list(), vec![]); + 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(); + assert_eq!(context.state.lock().users.get_list(), vec![]); + assert_eq!(context.state.lock().rooms.get_room_list(), vec![]); } struct Waiter { @@ -84,7 +92,11 @@ mod tests { #[test] fn join_waits_for_all_jobs() { - let executor = Executor::new(Context::new()); + let (tx, _rx) = channel(1); + let executor = Executor::new(Context { + state: Mutex::new(State::default()), + server_request_tx: tx, + }); let barrier = Arc::new(Barrier::new(2)); @@ -104,13 +116,17 @@ mod tests { impl Job for UserAdder { fn execute(self: Box, context: &Context) { - context.users.lock().insert(self.user); + context.state.lock().users.insert(self.user); } } #[test] fn jobs_access_context() { - let executor = Executor::new(Context::new()); + let (tx, _rx) = channel(1); + let executor = Executor::new(Context { + state: Mutex::new(State::default()), + server_request_tx: tx, + }); let user1 = User { name: "potato".to_string(), @@ -139,7 +155,7 @@ mod tests { let expected_users = vec![(user1.name.clone(), user1), (user2.name.clone(), user2)]; - let mut users = context.users.lock().get_list(); + let mut users = context.state.lock().users.get_list(); users.sort(); assert_eq!(users, expected_users); diff --git a/client/src/handlers/login_handler.rs b/client/src/handlers/login_handler.rs deleted file mode 100644 index 4798114..0000000 --- a/client/src/handlers/login_handler.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::io; - -use crate::context::Context; -use crate::login::LoginStatus; -use crate::message_handler::MessageHandler; -use solstice_proto::server::LoginResponse; - -#[derive(Debug, Default)] -pub struct LoginHandler; - -impl MessageHandler for LoginHandler { - fn run(self, context: &Context, _message: &LoginResponse) -> io::Result<()> { - let lock = context.login.lock(); - - match *lock { - LoginStatus::AwaitingResponse => (), - _ => { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("unexpected login response, status = {:?}", *lock), - )); - } - }; - - unimplemented!(); - } - - fn name() -> String { - "LoginHandler".to_string() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[should_panic] - fn run_fails_on_wrong_status() { - let context = Context::new(); - - let response = LoginResponse::LoginFail { - reason: "bleep bloop".to_string(), - }; - - LoginHandler::default().run(&context, &response).unwrap(); - } -} diff --git a/client/src/handlers/mod.rs b/client/src/handlers/mod.rs index b903112..92bfcd0 100644 --- a/client/src/handlers/mod.rs +++ b/client/src/handlers/mod.rs @@ -1,5 +1,3 @@ -mod login_handler; mod set_privileged_users_handler; -pub use login_handler::LoginHandler; pub use set_privileged_users_handler::SetPrivilegedUsersHandler; diff --git a/client/src/handlers/set_privileged_users_handler.rs b/client/src/handlers/set_privileged_users_handler.rs index 1fc35cf..189e725 100644 --- a/client/src/handlers/set_privileged_users_handler.rs +++ b/client/src/handlers/set_privileged_users_handler.rs @@ -1,8 +1,9 @@ use std::io; +use solstice_proto::server::PrivilegedUsersResponse; + use crate::context::Context; use crate::message_handler::MessageHandler; -use solstice_proto::server::PrivilegedUsersResponse; #[derive(Debug, Default)] pub struct SetPrivilegedUsersHandler; @@ -14,7 +15,7 @@ impl MessageHandler for SetPrivilegedUsersHandler { message: &PrivilegedUsersResponse, ) -> io::Result<()> { let users = message.users.clone(); - context.users.lock().set_all_privileged(users); + context.state.lock().users.set_all_privileged(users); Ok(()) } @@ -25,15 +26,22 @@ impl MessageHandler for SetPrivilegedUsersHandler { #[cfg(test)] mod tests { - use crate::context::Context; - use crate::message_handler::MessageHandler; + use parking_lot::Mutex; use solstice_proto::server::PrivilegedUsersResponse; + use tokio::sync::mpsc::channel; + + use crate::context::{Context, State}; + use crate::message_handler::MessageHandler; use super::SetPrivilegedUsersHandler; #[test] fn run_sets_privileged_users() { - let context = Context::new(); + let (tx, _rx) = channel(1); + let context = Context { + state: Mutex::new(State::default()), + server_request_tx: tx, + }; let response = PrivilegedUsersResponse { users: vec![ @@ -47,7 +55,7 @@ mod tests { .run(&context, &response) .unwrap(); - let mut privileged = context.users.lock().get_all_privileged(); + let mut privileged = 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 a3266ca..030dfd3 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -7,8 +7,9 @@ use std::thread; use clap::{App, Arg}; use crossbeam_channel; use env_logger; -use futures::stream::{Stream, StreamExt}; +use futures::stream::StreamExt; use log::info; +use parking_lot::Mutex; use solstice_proto; use tokio::net::TcpStream; @@ -23,7 +24,7 @@ mod message_handler; mod room; mod user; -use context::Context; +use context::{Context, State}; use dispatcher::Dispatcher; use executor::Executor; @@ -115,14 +116,17 @@ async fn run_client( } async fn async_main() { - let (_server_request_tx, server_request_rx) = tokio::sync::mpsc::channel(100); + let (server_request_tx, server_request_rx) = tokio::sync::mpsc::channel(100); let (dispatcher_tx, mut dispatcher_rx) = tokio::sync::mpsc::unbounded_channel(); let client_task = tokio::spawn(run_client(server_request_rx, dispatcher_tx)); let dispatcher = Dispatcher::new(); - let executor = Executor::new(Context::new()); + let executor = Executor::new(Context { + state: Mutex::new(State::default()), + server_request_tx, + }); while let Some(message) = dispatcher_rx.recv().await { if let Some(job) = dispatcher.dispatch(message) { diff --git a/client/src/room.rs b/client/src/room.rs index b291e74..ceff9c9 100644 --- a/client/src/room.rs +++ b/client/src/room.rs @@ -118,7 +118,7 @@ impl error::Error for Error { /// Contains the mapping from room names to room data and provides a clean /// interface to interact with it. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct RoomMap { /// The actual map from room names to room data. map: collections::HashMap, @@ -127,9 +127,7 @@ pub struct RoomMap { impl RoomMap { /// Creates an empty mapping. pub fn new() -> Self { - RoomMap { - map: collections::HashMap::new(), - } + Self::default() } /// Looks up the given room name in the map, returning an immutable diff --git a/client/src/user.rs b/client/src/user.rs index c45270a..cd999f1 100644 --- a/client/src/user.rs +++ b/client/src/user.rs @@ -25,7 +25,7 @@ impl error::Error for UserNotFoundError { /// Contains the mapping from user names to user data and provides a clean /// interface to interact with it. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct UserMap { /// The actual map from user names to user data and privileged status. map: collections::HashMap, @@ -36,10 +36,7 @@ pub struct UserMap { impl UserMap { /// Creates an empty mapping. pub fn new() -> Self { - UserMap { - map: collections::HashMap::new(), - privileged: collections::HashSet::new(), - } + Self::default() } /// Looks up the given user name in the map, returning an immutable