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.
 

211 lines
5.5 KiB

//! 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());
}
}