3 Commits

5 changed files with 243 additions and 349 deletions
Split View
  1. +0
    -6
      client/src/handlers/mod.rs
  2. +0
    -85
      client/src/handlers/room_list_response_handler.rs
  3. +0
    -74
      client/src/handlers/room_message_request_handler.rs
  4. +0
    -163
      client/src/handlers/room_message_response_handler.rs
  5. +243
    -21
      client/src/room/event.rs

+ 0
- 6
client/src/handlers/mod.rs View File

@ -2,16 +2,10 @@ mod login_status_request_handler;
mod peer_address_response_handler;
mod peer_connect_request_handler;
mod privileged_users_response_handler;
mod room_list_response_handler;
mod room_message_request_handler;
mod room_message_response_handler;
mod user_list_request_handler;
pub use login_status_request_handler::LoginStatusRequestHandler;
pub use peer_address_response_handler::PeerAddressResponseHandler;
pub use peer_connect_request_handler::PeerConnectRequestHandler;
pub use privileged_users_response_handler::PrivilegedUsersResponseHandler;
pub use room_list_response_handler::RoomListResponseHandler;
pub use room_message_request_handler::RoomMessageRequestHandler;
pub use room_message_response_handler::RoomMessageResponseHandler;
pub use user_list_request_handler::UserListRequestHandler;

+ 0
- 85
client/src/handlers/room_list_response_handler.rs View File

@ -1,85 +0,0 @@
use anyhow::Context as AnyhowContext;
use solstice_proto::server::RoomListResponse;
use crate::context::Context;
use crate::control;
use crate::message_handler::MessageHandler;
#[derive(Debug, Default)]
pub struct RoomListResponseHandler;
impl MessageHandler for RoomListResponseHandler {
type Message = RoomListResponse;
fn run(
self,
context: &mut Context,
message: &RoomListResponse,
) -> anyhow::Result<()> {
let response = message.clone();
context.state.rooms.set_room_list(response);
let rooms = context.state.rooms.get_room_list();
let control_response =
control::Response::RoomListResponse(control::RoomListResponse { rooms });
context
.control_response_tx
.blocking_send(control_response)
.context("sending control response")?;
Ok(())
}
fn name() -> String {
"RoomListResponseHandler".to_string()
}
}
#[cfg(test)]
mod tests {
use solstice_proto::server::RoomListResponse;
use crate::context::ContextBundle;
use crate::message_handler::MessageHandler;
use crate::room::{RoomState, RoomVisibility};
use super::RoomListResponseHandler;
// Cannot get the compiler to be satisfied when borrowing the name...
fn room_name(pair: &(String, RoomState)) -> String {
pair.0.clone()
}
#[test]
fn run_sets_room_list() {
let mut bundle = ContextBundle::default();
let response = RoomListResponse {
rooms: vec![("potato".to_string(), 123), ("apple".to_string(), 42)],
owned_private_rooms: vec![],
other_private_rooms: vec![],
operated_private_room_names: vec![],
};
RoomListResponseHandler::default()
.run(&mut bundle.context, &response)
.unwrap();
let mut rooms = bundle.context.state.rooms.get_room_list();
rooms.sort_by_key(room_name);
assert_eq!(
rooms,
vec![
(
"apple".to_string(),
RoomState::new(RoomVisibility::Public, 42)
),
(
"potato".to_string(),
RoomState::new(RoomVisibility::Public, 123)
),
]
);
}
}

+ 0
- 74
client/src/handlers/room_message_request_handler.rs View File

@ -1,74 +0,0 @@
use anyhow::Context as AnyhowContext;
use solstice_proto::server;
use solstice_proto::ServerRequest;
use crate::context::Context;
use crate::control;
use crate::message_handler::MessageHandler;
#[derive(Debug, Default)]
pub struct RoomMessageRequestHandler;
impl MessageHandler for RoomMessageRequestHandler {
type Message = control::RoomMessageRequest;
fn run(
self,
context: &mut Context,
message: &control::RoomMessageRequest,
) -> anyhow::Result<()> {
// TODO: Add message to room.
context
.server_request_tx
.blocking_send(ServerRequest::RoomMessageRequest(
server::RoomMessageRequest {
room_name: message.room_name.clone(),
message: message.message.clone(),
},
))
.context("sending server request")?;
Ok(())
}
fn name() -> String {
"RoomMessageRequestHandler".to_string()
}
}
#[cfg(test)]
mod tests {
use solstice_proto::server;
use solstice_proto::ServerRequest;
use crate::context::ContextBundle;
use crate::control;
use crate::message_handler::MessageHandler;
use super::RoomMessageRequestHandler;
#[test]
fn run_forwards_request() {
let mut bundle = ContextBundle::default();
RoomMessageRequestHandler::default()
.run(
&mut bundle.context,
&control::RoomMessageRequest {
room_name: "bleep".to_string(),
message: "yo!".to_string(),
}
.into(),
)
.unwrap();
let request = bundle.server_request_rx.blocking_recv().unwrap();
assert_eq!(
request,
ServerRequest::RoomMessageRequest(server::RoomMessageRequest {
room_name: "bleep".to_string(),
message: "yo!".to_string(),
})
);
}
}

+ 0
- 163
client/src/handlers/room_message_response_handler.rs View File

@ -1,163 +0,0 @@
use anyhow::Context as AnyhowContext;
use solstice_proto::server;
use crate::context::Context;
use crate::control;
use crate::message_handler::MessageHandler;
use crate::room::RoomMessage;
#[derive(Debug, Default)]
pub struct RoomMessageResponseHandler;
impl MessageHandler for RoomMessageResponseHandler {
type Message = server::RoomMessageResponse;
fn run(
self,
context: &mut Context,
response: &server::RoomMessageResponse,
) -> anyhow::Result<()> {
let room = context
.state
.rooms
.get_mut_strict(&response.room_name)
.context("looking up room")?;
let message = RoomMessage {
received_at: context.system_clock.now(),
user_name: response.user_name.clone(),
message: response.message.clone(),
};
room.insert_message(message.clone());
context
.control_response_tx
.blocking_send(control::Response::RoomMessageResponse(
control::RoomMessageResponse {
room_name: response.room_name.clone(),
message,
},
))
.context("sending control response")?;
Ok(())
}
fn name() -> String {
"RoomMessageResponseHandler".to_string()
}
}
#[cfg(test)]
mod tests {
use std::time::{Duration, SystemTime};
use solstice_proto::server;
use crate::clock::SimulatedSystemClock;
use crate::context::{ContextBundle, ContextOptions};
use crate::control;
use crate::message_handler::MessageHandler;
use crate::room::{RoomMessage, RoomState, RoomVisibility};
use super::RoomMessageResponseHandler;
fn system_time_from_secs(seconds: u64) -> SystemTime {
SystemTime::UNIX_EPOCH + Duration::from_secs(seconds)
}
#[test]
fn run_forwards_response() {
let mut options = ContextOptions::default();
options.initial_state.rooms.insert(
"apple".to_string(),
RoomState::new(RoomVisibility::Public, 1),
);
options.simulated_clock =
Some(SimulatedSystemClock::new(system_time_from_secs(42)));
let mut bundle = ContextBundle::new(options);
RoomMessageResponseHandler::default()
.run(
&mut bundle.context,
&server::RoomMessageResponse {
room_name: "apple".to_string(),
user_name: "shruti".to_string(),
message: "yo!".to_string(),
},
)
.expect("running handler");
let response = bundle
.control_response_rx
.blocking_recv()
.expect("receiving control response");
assert_eq!(
response,
control::Response::RoomMessageResponse(control::RoomMessageResponse {
room_name: "apple".to_string(),
message: RoomMessage {
user_name: "shruti".to_string(),
message: "yo!".to_string(),
received_at: SystemTime::UNIX_EPOCH + Duration::from_secs(42),
},
})
);
}
#[test]
fn run_stores_message() {
let mut room = RoomState::new(RoomVisibility::Public, 42);
room.messages.insert(RoomMessage {
received_at: system_time_from_secs(42),
user_name: "karandeep".to_string(),
message: "namaste!".to_string(),
});
let mut options = ContextOptions::default();
options
.initial_state
.rooms
.insert("apple".to_string(), room.clone());
options.simulated_clock =
Some(SimulatedSystemClock::new(system_time_from_secs(43)));
let mut bundle = ContextBundle::new(options);
RoomMessageResponseHandler::default()
.run(
&mut bundle.context,
&server::RoomMessageResponse {
room_name: "apple".to_string(),
user_name: "shruti".to_string(),
message: "yo!".to_string(),
},
)
.expect("running handler");
let room = bundle
.context
.state
.rooms
.get_strict("apple")
.expect("looking up room");
assert_eq!(
room.clone_state().messages.to_vec(),
vec![
RoomMessage {
received_at: system_time_from_secs(42),
user_name: "karandeep".to_string(),
message: "namaste!".to_string(),
},
RoomMessage {
received_at: system_time_from_secs(43),
user_name: "shruti".to_string(),
message: "yo!".to_string(),
},
]
);
}
}

+ 243
- 21
client/src/room/event.rs View File

@ -7,11 +7,7 @@ use solstice_proto::ServerRequest;
use crate::context::Context;
use crate::control;
use crate::handlers::{
RoomListResponseHandler, RoomMessageRequestHandler,
RoomMessageResponseHandler,
};
use crate::message_handler::MessageHandler;
use crate::room::RoomMessage;
/// An event affecting the chat room module.
#[derive(Debug, PartialEq, Eq)]
@ -72,9 +68,7 @@ fn handle_join_request(
.blocking_send(ServerRequest::RoomJoinRequest(server::RoomJoinRequest {
room_name,
}))
.context("sending server request")?;
Ok(())
.context("sending server request")
}
fn handle_join_response(
@ -94,8 +88,7 @@ fn handle_join_response(
context
.control_response_tx
.blocking_send(control_response)
.context("sending control response")?;
Ok(())
.context("sending control response")
}
fn handle_list_request(context: &mut Context) -> anyhow::Result<()> {
@ -111,8 +104,68 @@ fn handle_list_request(context: &mut Context) -> anyhow::Result<()> {
context
.server_request_tx
.blocking_send(ServerRequest::RoomListRequest)
.context("sending server request")?;
Ok(())
.context("sending server request")
}
fn handle_list_response(
context: &mut Context,
response: server::RoomListResponse,
) -> anyhow::Result<()> {
context.state.rooms.set_room_list(response);
let rooms = context.state.rooms.get_room_list();
let control_response =
control::Response::RoomListResponse(control::RoomListResponse { rooms });
context
.control_response_tx
.blocking_send(control_response)
.context("sending control response")
}
fn handle_message_request(
context: &mut Context,
message: control::RoomMessageRequest,
) -> anyhow::Result<()> {
// TODO: Add message to room.
context
.server_request_tx
.blocking_send(ServerRequest::RoomMessageRequest(
server::RoomMessageRequest {
room_name: message.room_name,
message: message.message,
},
))
.context("sending server request")
}
fn handle_message_response(
context: &mut Context,
response: server::RoomMessageResponse,
) -> anyhow::Result<()> {
let room = context
.state
.rooms
.get_mut_strict(&response.room_name)
.context("looking up room")?;
let message = RoomMessage {
received_at: context.system_clock.now(),
user_name: response.user_name,
message: response.message,
};
room.insert_message(message.clone());
context
.control_response_tx
.blocking_send(control::Response::RoomMessageResponse(
control::RoomMessageResponse {
room_name: response.room_name,
message,
},
))
.context("sending control response")
}
impl HandleRoomEvent for RoomEventHandler {
@ -131,13 +184,13 @@ impl HandleRoomEvent for RoomEventHandler {
}
RoomEvent::ListRequest => handle_list_request(context),
RoomEvent::ListResponse(response) => {
RoomListResponseHandler::default().run(context, &response)
handle_list_response(context, response)
}
RoomEvent::MessageRequest(request) => {
RoomMessageRequestHandler::default().run(context, &request)
handle_message_request(context, request)
}
RoomEvent::MessageResponse(response) => {
RoomMessageResponseHandler::default().run(context, &response)
handle_message_response(context, response)
}
}
}
@ -168,8 +221,12 @@ pub mod testing {
#[cfg(test)]
mod tests {
use std::cmp::Ordering;
use std::time::{Duration, SystemTime};
use solstice_proto::server;
use crate::clock::SimulatedSystemClock;
use crate::context::{ContextBundle, ContextOptions};
use crate::room::{RoomMembership, RoomState, RoomVisibility};
@ -327,6 +384,16 @@ mod tests {
assert_eq!(request, ServerRequest::RoomListRequest);
}
// `sort_by_key()` cannot handle an extractor function that returns
// references, so we use `sort_by()` and a comparator instead.
// See: https://github.com/rust-lang/rust/issues/34162
fn compare_by_name(
lhs: &(String, RoomState),
rhs: &(String, RoomState),
) -> Ordering {
lhs.0.cmp(&rhs.0)
}
#[test]
fn handle_list_request_sends_immediate_response() {
let mut options = ContextOptions::default();
@ -355,12 +422,40 @@ mod tests {
_ => panic!("Unexpected control response: {:?}", response),
};
// `sort_by_key()` cannot handle an extractor function that returns
// references, so we use `sort_by()` instead.
// See: https://github.com/rust-lang/rust/issues/34162
rooms.sort_unstable_by(|(ref lhs_name, _), (ref rhs_name, _)| {
lhs_name.cmp(rhs_name)
});
rooms.sort_unstable_by(compare_by_name);
assert_eq!(
rooms,
vec![
(
"apple".to_string(),
RoomState::new(RoomVisibility::Public, 42)
),
(
"potato".to_string(),
RoomState::new(RoomVisibility::Public, 123)
),
]
);
}
#[test]
fn handle_list_response_sets_room_list() {
let mut bundle = ContextBundle::default();
let response = server::RoomListResponse {
rooms: vec![("potato".to_string(), 123), ("apple".to_string(), 42)],
owned_private_rooms: vec![],
other_private_rooms: vec![],
operated_private_room_names: vec![],
};
RoomEventHandler
.handle(&mut bundle.context, RoomEvent::ListResponse(response))
.expect("handling response");
let mut rooms = bundle.context.state.rooms.get_room_list();
rooms.sort_unstable_by(compare_by_name);
assert_eq!(
rooms,
@ -376,4 +471,131 @@ mod tests {
]
);
}
#[test]
fn handle_message_request_forwards_request() {
let mut bundle = ContextBundle::default();
RoomEventHandler
.handle(
&mut bundle.context,
RoomEvent::MessageRequest(control::RoomMessageRequest {
room_name: "bleep".to_string(),
message: "yo!".to_string(),
}),
)
.expect("handling request");
let request = bundle
.server_request_rx
.blocking_recv()
.expect("receiving request");
assert_eq!(
request,
ServerRequest::RoomMessageRequest(server::RoomMessageRequest {
room_name: "bleep".to_string(),
message: "yo!".to_string(),
})
);
}
fn system_time_from_secs(seconds: u64) -> SystemTime {
SystemTime::UNIX_EPOCH + Duration::from_secs(seconds)
}
#[test]
fn handle_message_response_forwards_response() {
let mut options = ContextOptions::default();
options.initial_state.rooms.insert(
"apple".to_string(),
RoomState::new(RoomVisibility::Public, 1),
);
options.simulated_clock =
Some(SimulatedSystemClock::new(system_time_from_secs(42)));
let mut bundle = ContextBundle::new(options);
RoomEventHandler
.handle(
&mut bundle.context,
RoomEvent::MessageResponse(server::RoomMessageResponse {
room_name: "apple".to_string(),
user_name: "shruti".to_string(),
message: "yo!".to_string(),
}),
)
.expect("handling response");
let response = bundle
.control_response_rx
.blocking_recv()
.expect("receiving control response");
assert_eq!(
response,
control::Response::RoomMessageResponse(control::RoomMessageResponse {
room_name: "apple".to_string(),
message: RoomMessage {
user_name: "shruti".to_string(),
message: "yo!".to_string(),
received_at: SystemTime::UNIX_EPOCH + Duration::from_secs(42),
},
})
);
}
#[test]
fn handle_message_response_stores_message() {
let mut room = RoomState::new(RoomVisibility::Public, 42);
room.messages.insert(RoomMessage {
received_at: system_time_from_secs(42),
user_name: "karandeep".to_string(),
message: "namaste!".to_string(),
});
let mut options = ContextOptions::default();
options
.initial_state
.rooms
.insert("apple".to_string(), room.clone());
options.simulated_clock =
Some(SimulatedSystemClock::new(system_time_from_secs(43)));
let mut bundle = ContextBundle::new(options);
RoomEventHandler
.handle(
&mut bundle.context,
RoomEvent::MessageResponse(server::RoomMessageResponse {
room_name: "apple".to_string(),
user_name: "shruti".to_string(),
message: "yo!".to_string(),
}),
)
.expect("handling response");
let room = bundle
.context
.state
.rooms
.get_strict("apple")
.expect("looking up room");
assert_eq!(
room.clone_state().messages.to_vec(),
vec![
RoomMessage {
received_at: system_time_from_secs(42),
user_name: "karandeep".to_string(),
message: "namaste!".to_string(),
},
RoomMessage {
received_at: system_time_from_secs(43),
user_name: "shruti".to_string(),
message: "yo!".to_string(),
},
]
);
}
}

Loading…
Cancel
Save