Solstice client.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

336 lines
9.6 KiB

//! This module defines the central message dispatcher to the client process.
use log::{error, warn};
use solstice_proto::server::ServerResponse;
use crate::context::Context;
use crate::control::Request as ControlRequest;
use crate::event::{Message, EventHandler};
use crate::handlers::*;
use crate::message_handler::MessageHandler;
use crate::room::{RoomEvent, RoomEventHandler};
/// Subsystem event handlers to which the `Dispatcher` dispatches events.
pub trait DispatcherHandlers {
type RoomEventHandler: EventHandler<Event=RoomEvent>;
fn room_event_handler(&mut self) -> &mut Self::RoomEventHandler;
}
/// Default event handlers implementing real behavior.
#[derive(Default)]
pub struct DefaultDispatcherHandlers {
room_event_handler: RoomEventHandler,
}
impl DispatcherHandlers for DefaultDispatcherHandlers {
type RoomEventHandler = RoomEventHandler;
fn room_event_handler(&mut self) -> &mut Self::RoomEventHandler {
&mut self.room_event_handler
}
}
/// Parts that make up a `Dispatcher`.
pub struct DispatcherParts<H> {
/// The global context against which messages are handled.
pub context: Context,
/// Subsystem event handlers. Injected for testability.
pub handlers: H,
}
/// The `Dispatcher` is in charge of mapping messages to their handlers.
pub struct Dispatcher<H> {
pub context: Context,
pub handlers: H,
}
impl<H: DispatcherHandlers> Dispatcher<H> {
/// Returns a new `Dispatcher` from the given parts.
pub fn from_parts(parts: DispatcherParts<H>) -> Self {
Self {
context: parts.context,
handlers: parts.handlers,
}
}
/// Breaks down a `Dispatcher` into its constituent parts.
pub fn into_parts(self) -> DispatcherParts<H> {
DispatcherParts {
context: self.context,
handlers: self.handlers,
}
}
fn dispatch_internal(&mut self, message: Message) -> anyhow::Result<()> {
match message {
Message::ServerResponse(ServerResponse::PeerAddressResponse(
response,
)) => {
PeerAddressResponseHandler::default().run(&mut self.context, &response)
}
Message::ServerResponse(ServerResponse::PrivilegedUsersResponse(
response,
)) => PrivilegedUsersResponseHandler::default()
.run(&mut self.context, &response),
Message::ServerResponse(ServerResponse::RoomJoinResponse(response)) => {
self
.handlers
.room_event_handler()
.handle(&mut self.context, RoomEvent::JoinResponse(response))
}
Message::ServerResponse(ServerResponse::RoomMessageResponse(
response,
)) => self
.handlers
.room_event_handler()
.handle(&mut self.context, RoomEvent::MessageResponse(response)),
Message::ServerResponse(ServerResponse::RoomListResponse(response)) => {
self
.handlers
.room_event_handler()
.handle(&mut self.context, RoomEvent::ListResponse(response))
}
Message::ServerResponse(response) => {
warn!("Unhandled server response: {:?}", response);
Ok(())
}
Message::ControlRequest(ControlRequest::LoginStatusRequest) => {
LoginStatusRequestHandler::default().run(&mut self.context, &())
}
Message::ControlRequest(ControlRequest::PeerConnectRequest(request)) => {
PeerConnectRequestHandler::default().run(&mut self.context, &request)
}
Message::ControlRequest(ControlRequest::RoomListRequest) => self
.handlers
.room_event_handler()
.handle(&mut self.context, RoomEvent::ListRequest),
Message::ControlRequest(ControlRequest::RoomMessageRequest(request)) => {
self
.handlers
.room_event_handler()
.handle(&mut self.context, RoomEvent::MessageRequest(request))
}
Message::ControlRequest(ControlRequest::RoomJoinRequest(room_name)) => {
self
.handlers
.room_event_handler()
.handle(&mut self.context, RoomEvent::JoinRequest(room_name))
}
Message::ControlRequest(ControlRequest::UserListRequest) => {
UserListRequestHandler::default().run(&mut self.context, &())
}
Message::ControlRequest(request) => {
warn!("Unhandled control request: {:?}", request);
Ok(())
}
}
}
/// Dispatches the given message to the appropriate subsystem handler.
pub fn dispatch(&mut self, message: Message) {
let debug_message = format!("{:?}", &message);
if let Err(error) = self.dispatch_internal(message) {
error!(
"Error handling message: {:?}\nMessage: {}",
error, debug_message
);
}
}
}
#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;
use solstice_proto::server;
use crate::context::ContextBundle;
use crate::control;
use crate::dispatcher::Message;
use crate::room::testing::FakeRoomEventHandler;
use super::*;
#[derive(Default)]
struct FakeDispatcherHandlers {
room_event_handler: FakeRoomEventHandler,
}
impl FakeDispatcherHandlers {
// Defined here for better maintainability as new handlers are added.
fn has_events(&self) -> bool {
!self.room_event_handler.events.is_empty()
}
}
impl DispatcherHandlers for FakeDispatcherHandlers {
type RoomEventHandler = FakeRoomEventHandler;
fn room_event_handler(&mut self) -> &mut Self::RoomEventHandler {
&mut self.room_event_handler
}
}
/// Dispatches `message` to fake handlers using a new `Dispatcher` and
/// `Context`, then returns the handlers for inspection.
fn dispatch(message: Message) -> FakeDispatcherHandlers {
let bundle = ContextBundle::default();
let mut dispatcher = Dispatcher::from_parts(DispatcherParts {
context: bundle.context,
handlers: FakeDispatcherHandlers::default(),
});
dispatcher.dispatch(message);
dispatcher.into_parts().handlers
}
#[test]
fn does_not_dispatch_unhandled_response() {
let response = server::LoginResponse::LoginFail {
reason: "bleep bloop".to_string(),
};
let handlers = dispatch(Message::ServerResponse(response.into()));
assert!(!handlers.has_events());
}
#[test]
fn dispatches_login_status_request() {
let request = control::Request::LoginStatusRequest;
let _handlers = dispatch(Message::ControlRequest(request));
// TODO: Check that event is dispatched to a new login event handler.
}
#[test]
fn dispatches_peer_address_response() {
let response = server::PeerAddressResponse {
user_name: "shruti".to_string(),
ip: Ipv4Addr::new(1, 2, 3, 4),
port: 1234,
};
let _handlers = dispatch(Message::ServerResponse(response.into()));
// TODO: Check that event is dispatched to a new peer event handler.
}
#[test]
fn dispatches_peer_connect_request() {
let request = control::Request::PeerConnectRequest("shruti".to_string());
let _handlers = dispatch(Message::ControlRequest(request));
// TODO: Check that event is dispatched to a new peer event handler.
}
#[test]
fn dispatches_privileged_users_response() {
let response = server::PrivilegedUsersResponse {
users: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()],
};
let _handlers = dispatch(Message::ServerResponse(response.into()));
// TODO: Check that event is dispatched to a new user event handler.
}
#[test]
fn dispatches_room_join_request() {
let request = control::Request::RoomJoinRequest("bleep".to_string());
let handlers = dispatch(Message::ControlRequest(request.into()));
assert_eq!(
handlers.room_event_handler.events,
&[RoomEvent::JoinRequest("bleep".to_string())],
);
}
#[test]
fn dispatches_room_join_response() {
let response = server::RoomJoinResponse {
room_name: "bleep".to_string(),
owner: None,
operators: Vec::new(),
users: Vec::new(),
};
let handlers = dispatch(Message::ServerResponse(response.clone().into()));
assert_eq!(
handlers.room_event_handler.events,
&[RoomEvent::JoinResponse(response)]
);
}
#[test]
fn dispatches_room_list_request() {
let handlers = dispatch(control::Request::RoomListRequest.into());
assert_eq!(
handlers.room_event_handler.events,
&[RoomEvent::ListRequest]
);
}
#[test]
fn dispatches_room_list_response() {
let response = server::RoomListResponse {
rooms: Vec::new(),
owned_private_rooms: Vec::new(),
other_private_rooms: Vec::new(),
operated_private_room_names: Vec::new(),
};
let handlers = dispatch(Message::ServerResponse(response.clone().into()));
assert_eq!(
handlers.room_event_handler.events,
&[RoomEvent::ListResponse(response)]
);
}
#[test]
fn dispatches_room_message_request() {
let request = control::RoomMessageRequest {
room_name: "bleep".to_string(),
message: "yo!".to_string(),
};
let handlers = dispatch(Message::ControlRequest(request.clone().into()));
assert_eq!(
handlers.room_event_handler.events,
&[RoomEvent::MessageRequest(request)],
);
}
#[test]
fn dispatches_room_message_response() {
let response = server::RoomMessageResponse {
room_name: "bleep".to_string(),
user_name: "shruti".to_string(),
message: "yo!".to_string(),
};
let handlers = dispatch(Message::ServerResponse(response.clone().into()));
assert_eq!(
handlers.room_event_handler.events,
&[RoomEvent::MessageResponse(response)]
);
}
#[test]
fn dispatches_user_list_request() {
let request = control::Request::UserListRequest;
let _handlers = dispatch(request.into());
// TODO: Check that event is dispatched to a new user event handler.
}
}