use serde::Serialize;
use serde_json::Value;
use rainier_support::{Error, Result};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Message {
Text(String),
Binary(Vec<u8>),
Close(Option<String>),
}
impl Message {
pub fn text(body: impl Into<String>) -> Self {
Message::Text(body.into())
}
pub fn binary(body: impl Into<Vec<u8>>) -> Self {
Message::Binary(body.into())
}
pub fn json(value: &impl Serialize) -> Result<Self> {
serde_json::to_string(value)
.map(Message::Text)
.map_err(|e| Error::internal(format!("a socket message must serialise: {e}")))
}
pub fn as_text(&self) -> Option<&str> {
match self {
Message::Text(text) => Some(text),
_ => None,
}
}
pub fn as_bytes(&self) -> &[u8] {
match self {
Message::Text(text) => text.as_bytes(),
Message::Binary(bytes) => bytes,
Message::Close(reason) => reason.as_deref().unwrap_or("").as_bytes(),
}
}
pub fn parse_json(&self) -> Result<Value> {
let text = self
.as_text()
.ok_or_else(|| Error::bad_request("expected a text frame, got a binary one."))?;
serde_json::from_str(text)
.map_err(|e| Error::bad_request(format!("that is not valid JSON: {e}")))
}
pub fn is_close(&self) -> bool {
matches!(self, Message::Close(_))
}
pub fn len(&self) -> usize {
self.as_bytes().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl From<&str> for Message {
fn from(text: &str) -> Self {
Message::text(text)
}
}
impl From<String> for Message {
fn from(text: String) -> Self {
Message::Text(text)
}
}
impl From<Vec<u8>> for Message {
fn from(bytes: Vec<u8>) -> Self {
Message::Binary(bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn json_round_trips_through_a_text_frame() {
let message = Message::json(&serde_json::json!({ "room": 1 })).unwrap();
assert!(matches!(message, Message::Text(_)));
assert_eq!(message.parse_json().unwrap()["room"], 1);
}
#[test]
fn nonsense_from_a_client_is_a_bad_request_not_a_500() {
let err = Message::text("{not json").parse_json().unwrap_err();
assert_eq!(err.status(), 400, "{}", err.message());
}
#[test]
fn asking_a_binary_frame_for_json_says_which_kind_it_was() {
let err = Message::binary(vec![1, 2, 3]).parse_json().unwrap_err();
assert!(err.message().contains("binary"), "{}", err.message());
}
}