//! Abstraction for the messaging system to increase overall type safety and //! code readability. use std::sync::Arc; use axum::extract::ws::{Message, WebSocket}; use futures::{stream::SplitSink, SinkExt}; use messenger_common::{client::MessageType as ClientMessageType, server::MessageType}; use tokio::sync::broadcast::Sender; use tracing::error; use crate::app::State; /// Represents messages which can be sent through a [`WebSocket`]. pub trait Server where Self: std::marker::Sized + serde::Serialize, { /// Adds a message to the message history of the app state. async fn append_to_history(&self, state: &Arc) -> serde_json::Result<()>; /// Performs serialization of a message. /// Semantic alternative to `serde_json::to_string()`. fn serialize(&self) -> serde_json::Result { serde_json::to_string(&self) } /// Sends a message through the provided sender. /// /// The main purpose of this function is to enforce type-safety when sending /// a message. This prevents accidentally sending non-`MessageType` messages /// through the server. fn send(&self, tx: &Sender); } impl Server for MessageType { /// Adds a message to the message history of the app state. async fn append_to_history(&self, state: &Arc) -> serde_json::Result<()> { // Join and leave messages shouldn't be saved to history // We ignore the inner message here as we want to serialize the entire message if let Self::UserMessage(..) = *self { let mut history_guard = state.message_history.lock().await; // Only save the last 100 messages if history_guard.len() > 99 { history_guard.pop_front(); } // Add a new message to the back of the queue history_guard.push_back(serde_json::to_string(self)?); } Ok(()) } fn serialize(&self) -> serde_json::Result { serde_json::to_string(&self) } fn send(&self, tx: &Sender) { tracing::debug!("sending message, content: {:?}", &self); match self.serialize() { Ok(json_message) => { if let Err(error) = tx.send(json_message) { error!("error occurred while sending a message through a channel: {error}"); } } Err(error) => { error!("error occurred while converting message to json: {error}"); } }; } } /// Performs deserialization of a message. /// Semantic alternative to `serde_json::from_str::()`. pub fn deserialize(message: &str) -> serde_json::Result { serde_json::from_str(message) } /// Sends an error through a websocket to the client. /// Contains error handling to reduce overall code bloat. pub async fn send_error( sender: &mut SplitSink, error: messenger_common::server::Error, ) { // Log error through tracing to show invalid client behaviour error!("received message from client that is considered an error: {error}"); // Handle deserialization errors correctly to avoid a panic match MessageType::Error(error).serialize() { Ok(outbound_error) => { if let Err(error) = sender.send(Message::Text(outbound_error)).await { error!("unable to send error message through a websocket: {error}"); } } Err(error) => { // Errors can also occur during serialization, so these should be covered error!("unable to serialize outbound error message: {error}"); } }; }