use bytes::Bytes;
use serde::de::DeserializeOwned;
use serde_json::Value;
use crate::{Error, NumberString};
use super::arg::Arg;
use super::error::WsError;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum WsEvent {
Subscribed(Arg),
Unsubscribed(Arg),
Login,
Error {
code: String,
msg: String,
},
Notice(WsNotice),
ChannelConnectionCount(WsChannelConnectionCount),
ChannelConnectionCountError(WsChannelConnectionCount),
Operation(WsOperation),
Push(Push),
Reconnected,
Disconnected,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct WsNotice {
pub code: String,
pub msg: String,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct WsChannelConnectionCount {
pub channel: String,
pub conn_count: NumberString,
pub conn_id: String,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct WsOperation {
pub id: Option<String>,
pub op: String,
pub code: String,
pub msg: String,
pub in_time: Option<NumberString>,
pub out_time: Option<NumberString>,
raw: Bytes,
}
impl WsOperation {
pub fn parse<T: DeserializeOwned>(&self) -> Result<Vec<T>, Error> {
serde_json::from_slice(&self.raw)
.map_err(|source| WsError::Decode {
context: "operation".to_owned(),
source,
})
.map_err(Error::from)
}
pub fn raw_data(&self) -> &Bytes {
&self.raw
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Push {
pub arg: Arg,
pub action: Option<String>,
raw: Bytes,
}
impl Push {
pub(crate) fn new(arg: Arg, action: Option<String>, raw: Bytes) -> Self {
Self { arg, action, raw }
}
pub fn parse<T: DeserializeOwned>(&self) -> Result<Vec<T>, Error> {
serde_json::from_slice(&self.raw)
.map_err(|source| WsError::Decode {
context: "push".to_owned(),
source,
})
.map_err(Error::from)
}
pub fn raw_data(&self) -> &Bytes {
&self.raw
}
}
pub(crate) fn parse_text_event(text: &str) -> Result<Option<WsEvent>, Error> {
let value: Value = serde_json::from_str(text).map_err(|source| WsError::Decode {
context: text.chars().take(40).collect(),
source,
})?;
match value.get("event").and_then(Value::as_str) {
Some("subscribe") => {
let arg = parse_arg(&value)?;
Ok(Some(WsEvent::Subscribed(arg)))
}
Some("unsubscribe") => {
let arg = parse_arg(&value)?;
Ok(Some(WsEvent::Unsubscribed(arg)))
}
Some("login") => Ok(Some(WsEvent::Login)),
Some("error") => Ok(Some(WsEvent::Error {
code: string_field(&value, "code"),
msg: string_field(&value, "msg"),
})),
Some("notice") => Ok(Some(WsEvent::Notice(WsNotice {
code: string_field(&value, "code"),
msg: string_field(&value, "msg"),
}))),
Some("channel-conn-count") => Ok(Some(WsEvent::ChannelConnectionCount(
parse_channel_connection_count(&value),
))),
Some("channel-conn-count-error") => Ok(Some(WsEvent::ChannelConnectionCountError(
parse_channel_connection_count(&value),
))),
_ if value.get("arg").is_some() && value.get("data").is_some() => {
let arg = parse_arg(&value)?;
let raw = data_bytes(&value)?;
Ok(Some(WsEvent::Push(Push::new(
arg,
value
.get("action")
.and_then(Value::as_str)
.map(ToOwned::to_owned),
raw,
))))
}
_ if value.get("op").is_some() => Ok(Some(WsEvent::Operation(parse_operation(&value)?))),
_ => Ok(None),
}
}
fn parse_channel_connection_count(value: &Value) -> WsChannelConnectionCount {
WsChannelConnectionCount {
channel: string_field(value, "channel"),
conn_count: string_field(value, "connCount").into(),
conn_id: string_field(value, "connId"),
}
}
fn parse_operation(value: &Value) -> Result<WsOperation, Error> {
Ok(WsOperation {
id: value
.get("id")
.and_then(Value::as_str)
.map(ToOwned::to_owned),
op: string_field(value, "op"),
code: string_field(value, "code"),
msg: string_field(value, "msg"),
in_time: optional_number_string(value, "inTime"),
out_time: optional_number_string(value, "outTime"),
raw: data_bytes(value)?,
})
}
fn parse_arg(value: &Value) -> Result<Arg, Error> {
let arg = value.get("arg").ok_or_else(|| WsError::MissingArg {
raw: value_snippet(value),
})?;
let channel_name = channel_name(value);
serde_json::from_value(arg.clone())
.map_err(|source| WsError::Decode {
context: format!("channel {channel_name}"),
source,
})
.map_err(Error::from)
}
fn data_bytes(value: &Value) -> Result<Bytes, Error> {
let data = value
.get("data")
.cloned()
.unwrap_or_else(|| Value::Array(vec![]));
let raw = serde_json::to_vec(&data)
.map_err(|source| WsError::Encode { source })
.map_err(Error::from)?;
Ok(Bytes::from(raw))
}
fn channel_name(value: &Value) -> String {
value
.get("arg")
.and_then(|arg| arg.get("channel"))
.and_then(Value::as_str)
.unwrap_or("<unknown>")
.to_owned()
}
fn value_snippet(value: &Value) -> String {
raw_snippet(&value.to_string())
}
fn raw_snippet(raw: &str) -> String {
const MAX_RAW_SNIPPET_CHARS: usize = 200;
let mut snippet: String = raw.chars().take(MAX_RAW_SNIPPET_CHARS).collect();
if raw.chars().count() > MAX_RAW_SNIPPET_CHARS {
snippet.push('…');
}
snippet
}
fn string_field(value: &Value, field: &str) -> String {
value
.get(field)
.and_then(Value::as_str)
.unwrap_or_default()
.to_owned()
}
fn optional_number_string(value: &Value, field: &str) -> Option<NumberString> {
value.get(field).and_then(Value::as_str).map(Into::into)
}