//! This module defines the central message dispatcher to the client process.
|
|
|
|
use std::fmt::Debug;
|
|
|
|
use log::warn;
|
|
use solstice_proto::server::ServerResponse;
|
|
|
|
use crate::context::Context;
|
|
use crate::control::Request as ControlRequest;
|
|
use crate::executor::Job;
|
|
use crate::handlers::{
|
|
RoomJoinRequestHandler, RoomJoinResponseHandler, RoomListRequestHandler,
|
|
RoomMessageRequestHandler, SetPrivilegedUsersHandler, SetRoomListHandler,
|
|
};
|
|
use crate::message_handler::MessageHandler;
|
|
|
|
/// The type of messages dispatched by a dispatcher.
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
pub enum Message {
|
|
ControlRequest(ControlRequest),
|
|
ServerResponse(ServerResponse),
|
|
}
|
|
|
|
impl From<ServerResponse> for Message {
|
|
fn from(response: ServerResponse) -> Self {
|
|
Self::ServerResponse(response)
|
|
}
|
|
}
|
|
|
|
impl From<ControlRequest> for Message {
|
|
fn from(request: ControlRequest) -> Self {
|
|
Self::ControlRequest(request)
|
|
}
|
|
}
|
|
|
|
/// Pairs together a message and its handler as chosen by the dispatcher.
|
|
/// Implements Job so as to be scheduled on an executor.
|
|
struct DispatchedMessage<H, M> {
|
|
message: M,
|
|
handler: H,
|
|
}
|
|
|
|
impl<H> Job for DispatchedMessage<H, <H as MessageHandler>::Message>
|
|
where
|
|
H: MessageHandler + Send,
|
|
<H as MessageHandler>::Message: Debug + Send,
|
|
{
|
|
fn execute(self: Box<Self>, context: &Context) {
|
|
if let Err(error) = self.handler.run(context, &self.message) {
|
|
error!(
|
|
"Error in handler {}: {:?}\nMessage: {:?}",
|
|
H::name(),
|
|
error,
|
|
&self.message
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The Dispatcher is in charge of mapping messages to their handlers.
|
|
pub struct Dispatcher;
|
|
|
|
impl Dispatcher {
|
|
/// Returns a new dispatcher.
|
|
pub fn new() -> Self {
|
|
Self {}
|
|
}
|
|
|
|
/// Dispatches the given message by wrapping it with a handler.
|
|
pub fn dispatch(&self, message: Message) -> Option<Box<dyn Job>> {
|
|
match message {
|
|
Message::ServerResponse(ServerResponse::PrivilegedUsersResponse(
|
|
response,
|
|
)) => Some(Box::new(DispatchedMessage {
|
|
message: response,
|
|
handler: SetPrivilegedUsersHandler::default(),
|
|
})),
|
|
Message::ServerResponse(ServerResponse::RoomJoinResponse(response)) => {
|
|
Some(Box::new(DispatchedMessage {
|
|
message: response,
|
|
handler: RoomJoinResponseHandler::default(),
|
|
}))
|
|
}
|
|
Message::ServerResponse(ServerResponse::RoomListResponse(response)) => {
|
|
Some(Box::new(DispatchedMessage {
|
|
message: response,
|
|
handler: SetRoomListHandler::default(),
|
|
}))
|
|
}
|
|
Message::ServerResponse(response) => {
|
|
warn!("Unhandled server response: {:?}", response);
|
|
None
|
|
}
|
|
Message::ControlRequest(ControlRequest::RoomListRequest) => {
|
|
Some(Box::new(DispatchedMessage {
|
|
message: (),
|
|
handler: RoomListRequestHandler::default(),
|
|
}))
|
|
}
|
|
Message::ControlRequest(ControlRequest::RoomMessageRequest(request)) => {
|
|
Some(Box::new(DispatchedMessage {
|
|
message: request,
|
|
handler: RoomMessageRequestHandler::default(),
|
|
}))
|
|
}
|
|
Message::ControlRequest(ControlRequest::RoomJoinRequest(room_name)) => {
|
|
Some(Box::new(DispatchedMessage {
|
|
message: room_name,
|
|
handler: RoomJoinRequestHandler::default(),
|
|
}))
|
|
}
|
|
Message::ControlRequest(request) => {
|
|
warn!("Unhandled control request: {:?}", request);
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::control;
|
|
use crate::dispatcher::Message;
|
|
|
|
use solstice_proto::server;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn does_not_dispatch_unhandled_response() {
|
|
assert!(Dispatcher::new()
|
|
.dispatch(Message::ServerResponse(
|
|
server::LoginResponse::LoginFail {
|
|
reason: "bleep bloop".to_string(),
|
|
}
|
|
.into()
|
|
))
|
|
.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn dispatches_privileged_users_response() {
|
|
assert!(Dispatcher::new()
|
|
.dispatch(Message::ServerResponse(
|
|
server::PrivilegedUsersResponse {
|
|
users: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()],
|
|
}
|
|
.into()
|
|
))
|
|
.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn dispatches_room_join_response() {
|
|
assert!(Dispatcher::new()
|
|
.dispatch(Message::ServerResponse(
|
|
server::RoomJoinResponse {
|
|
room_name: "bleep".to_string(),
|
|
owner: None,
|
|
operators: Vec::new(),
|
|
users: Vec::new(),
|
|
}
|
|
.into()
|
|
))
|
|
.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn dispatches_room_list_response() {
|
|
assert!(Dispatcher::new()
|
|
.dispatch(Message::ServerResponse(
|
|
server::RoomListResponse {
|
|
rooms: Vec::new(),
|
|
owned_private_rooms: Vec::new(),
|
|
other_private_rooms: Vec::new(),
|
|
operated_private_room_names: Vec::new(),
|
|
}
|
|
.into()
|
|
))
|
|
.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn dispatches_room_join_request() {
|
|
assert!(Dispatcher::new()
|
|
.dispatch(Message::ControlRequest(
|
|
control::Request::RoomJoinRequest("bleep".to_string()).into()
|
|
))
|
|
.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn dispatches_room_message_request() {
|
|
assert!(Dispatcher::new()
|
|
.dispatch(Message::ControlRequest(
|
|
control::RoomMessageRequest {
|
|
room_name: "bleep".to_string(),
|
|
message: "yo!".to_string(),
|
|
}
|
|
.into()
|
|
))
|
|
.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn dispatches_room_list_request() {
|
|
assert!(Dispatcher::new()
|
|
.dispatch(Message::ControlRequest(control::Request::RoomListRequest))
|
|
.is_some());
|
|
}
|
|
}
|