use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct WsMessage {
pub route: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub payload: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(rename = "refId", skip_serializing_if = "Option::is_none")]
pub ref_id: Option<String>,
}
impl WsMessage {
pub fn new(route: impl Into<String>, payload: serde_json::Value) -> Self {
Self {
route: route.into(),
payload: Some(payload),
id: Some(uuid::Uuid::new_v4().to_string()),
ref_id: None,
}
}
pub fn route_only(route: impl Into<String>) -> Self {
Self {
route: route.into(),
payload: None,
id: Some(uuid::Uuid::new_v4().to_string()),
ref_id: None,
}
}
pub fn sign_in(token: &str) -> Self {
Self::new(
"@user/signIn",
serde_json::json!({
"token": token,
}),
)
}
pub fn sign_out() -> Self {
Self::route_only("@user/signOut")
}
pub fn set_status(
status: &str,
duration: Option<u64>,
activity: Option<serde_json::Value>,
) -> Self {
let mut payload = serde_json::json!({
"status": status,
});
if let Some(d) = duration {
payload["duration"] = serde_json::json!(d);
}
if let Some(a) = activity {
payload["activity"] = a;
}
Self::new("@user/setStatus", payload)
}
}
#[derive(Debug, Clone)]
pub(crate) struct ParsedRoute {
pub module: String,
pub action: String,
pub parameter: Option<String>,
}
impl ParsedRoute {
pub fn parse(route: &str) -> Option<Self> {
if !route.starts_with('@') {
return None;
}
let route = &route[1..];
let parts: Vec<&str> = route.splitn(2, '/').collect();
if parts.len() < 2 {
return None;
}
let module = format!("@{}", parts[0]);
let action_part = parts[1];
if let Some(colon_pos) = action_part.find(':') {
let action = action_part[..colon_pos].to_string();
let parameter = Some(action_part[colon_pos + 1..].to_string());
Some(Self {
module,
action,
parameter,
})
} else {
Some(Self {
module,
action: action_part.to_string(),
parameter: None,
})
}
}
#[allow(dead_code)]
pub fn full_action(&self) -> String {
format!("{}/{}", self.module, self.action)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_route_simple() {
let route = ParsedRoute::parse("@user/signIn").unwrap();
assert_eq!(route.module, "@user");
assert_eq!(route.action, "signIn");
assert!(route.parameter.is_none());
}
#[test]
fn test_parse_route_with_parameter() {
let route = ParsedRoute::parse("@user/signIn:ok").unwrap();
assert_eq!(route.module, "@user");
assert_eq!(route.action, "signIn");
assert_eq!(route.parameter, Some("ok".to_string()));
}
#[test]
fn test_parse_reports_route() {
let route = ParsedRoute::parse("@reports/onlineCount").unwrap();
assert_eq!(route.module, "@reports");
assert_eq!(route.action, "onlineCount");
}
#[test]
fn test_message_creation() {
let msg = WsMessage::sign_in("test-token");
assert_eq!(msg.route, "@user/signIn");
assert!(msg.payload.is_some());
assert!(msg.id.is_some());
}
}