1use bytes::Bytes;
4use serde::de::DeserializeOwned;
5use serde_json::Value;
6
7use crate::{Error, NumberString};
8
9use super::arg::Arg;
10use super::error::WsError;
11
12#[derive(Debug, Clone)]
16#[non_exhaustive]
17pub enum WsEvent {
18 Subscribed(Arg),
20 Unsubscribed(Arg),
22 Login,
24 Error {
26 code: String,
28 msg: String,
30 },
31 Notice(WsNotice),
33 ChannelConnectionCount(WsChannelConnectionCount),
35 ChannelConnectionCountError(WsChannelConnectionCount),
37 Operation(WsOperation),
39 Push(Push),
41 Reconnected,
43 Disconnected,
45}
46
47#[derive(Debug, Clone, Default)]
51#[non_exhaustive]
52pub struct WsNotice {
53 pub code: String,
55 pub msg: String,
57}
58
59#[derive(Debug, Clone)]
63#[non_exhaustive]
64pub struct WsChannelConnectionCount {
65 pub channel: String,
67 pub conn_count: NumberString,
69 pub conn_id: String,
71}
72
73#[derive(Debug, Clone)]
77#[non_exhaustive]
78pub struct WsOperation {
79 pub id: Option<String>,
81 pub op: String,
83 pub code: String,
85 pub msg: String,
87 pub in_time: Option<NumberString>,
89 pub out_time: Option<NumberString>,
91 raw: Bytes,
92}
93
94impl WsOperation {
95 pub fn parse<T: DeserializeOwned>(&self) -> Result<Vec<T>, Error> {
97 serde_json::from_slice(&self.raw)
98 .map_err(|source| WsError::Decode {
99 context: "operation".to_owned(),
100 source,
101 })
102 .map_err(Error::from)
103 }
104
105 pub fn raw_data(&self) -> &Bytes {
107 &self.raw
108 }
109}
110
111#[derive(Debug, Clone)]
118#[non_exhaustive]
119pub struct Push {
120 pub arg: Arg,
122 pub action: Option<String>,
124 raw: Bytes,
125}
126
127impl Push {
128 pub(crate) fn new(arg: Arg, action: Option<String>, raw: Bytes) -> Self {
129 Self { arg, action, raw }
130 }
131
132 pub fn parse<T: DeserializeOwned>(&self) -> Result<Vec<T>, Error> {
134 serde_json::from_slice(&self.raw)
135 .map_err(|source| WsError::Decode {
136 context: "push".to_owned(),
137 source,
138 })
139 .map_err(Error::from)
140 }
141
142 pub fn raw_data(&self) -> &Bytes {
144 &self.raw
145 }
146}
147
148pub(crate) fn parse_text_event(text: &str) -> Result<Option<WsEvent>, Error> {
149 let value: Value = serde_json::from_str(text).map_err(|source| WsError::Decode {
150 context: text.chars().take(40).collect(),
151 source,
152 })?;
153 match value.get("event").and_then(Value::as_str) {
154 Some("subscribe") => {
155 let arg = parse_arg(&value)?;
156 Ok(Some(WsEvent::Subscribed(arg)))
157 }
158 Some("unsubscribe") => {
159 let arg = parse_arg(&value)?;
160 Ok(Some(WsEvent::Unsubscribed(arg)))
161 }
162 Some("login") => Ok(Some(WsEvent::Login)),
163 Some("error") => Ok(Some(WsEvent::Error {
164 code: string_field(&value, "code"),
165 msg: string_field(&value, "msg"),
166 })),
167 Some("notice") => Ok(Some(WsEvent::Notice(WsNotice {
168 code: string_field(&value, "code"),
169 msg: string_field(&value, "msg"),
170 }))),
171 Some("channel-conn-count") => Ok(Some(WsEvent::ChannelConnectionCount(
172 parse_channel_connection_count(&value),
173 ))),
174 Some("channel-conn-count-error") => Ok(Some(WsEvent::ChannelConnectionCountError(
175 parse_channel_connection_count(&value),
176 ))),
177 _ if value.get("arg").is_some() && value.get("data").is_some() => {
178 let arg = parse_arg(&value)?;
179 let raw = data_bytes(&value)?;
180 Ok(Some(WsEvent::Push(Push::new(
181 arg,
182 value
183 .get("action")
184 .or_else(|| value.get("eventType"))
185 .and_then(Value::as_str)
186 .map(ToOwned::to_owned),
187 raw,
188 ))))
189 }
190 _ if value.get("op").is_some() => Ok(Some(WsEvent::Operation(parse_operation(&value)?))),
191 _ => Ok(None),
192 }
193}
194
195fn parse_channel_connection_count(value: &Value) -> WsChannelConnectionCount {
196 WsChannelConnectionCount {
197 channel: string_field(value, "channel"),
198 conn_count: string_field(value, "connCount").into(),
199 conn_id: string_field(value, "connId"),
200 }
201}
202
203fn parse_operation(value: &Value) -> Result<WsOperation, Error> {
204 Ok(WsOperation {
205 id: value
206 .get("id")
207 .and_then(Value::as_str)
208 .map(ToOwned::to_owned),
209 op: string_field(value, "op"),
210 code: string_field(value, "code"),
211 msg: string_field(value, "msg"),
212 in_time: optional_number_string(value, "inTime"),
213 out_time: optional_number_string(value, "outTime"),
214 raw: data_bytes(value)?,
215 })
216}
217
218fn parse_arg(value: &Value) -> Result<Arg, Error> {
219 let arg = value.get("arg").ok_or_else(|| WsError::MissingArg {
220 raw: value_snippet(value),
221 })?;
222 let channel_name = channel_name(value);
223 serde_json::from_value(arg.clone())
224 .map_err(|source| WsError::Decode {
225 context: format!("channel {channel_name}"),
226 source,
227 })
228 .map_err(Error::from)
229}
230
231fn data_bytes(value: &Value) -> Result<Bytes, Error> {
232 let data = value
233 .get("data")
234 .cloned()
235 .unwrap_or_else(|| Value::Array(vec![]));
236 let raw = serde_json::to_vec(&data)
237 .map_err(|source| WsError::Encode { source })
238 .map_err(Error::from)?;
239 Ok(Bytes::from(raw))
240}
241
242fn channel_name(value: &Value) -> String {
243 value
244 .get("arg")
245 .and_then(|arg| arg.get("channel"))
246 .and_then(Value::as_str)
247 .unwrap_or("<unknown>")
248 .to_owned()
249}
250
251fn value_snippet(value: &Value) -> String {
252 raw_snippet(&value.to_string())
253}
254
255fn raw_snippet(raw: &str) -> String {
256 const MAX_RAW_SNIPPET_CHARS: usize = 200;
257 let mut snippet: String = raw.chars().take(MAX_RAW_SNIPPET_CHARS).collect();
258 if raw.chars().count() > MAX_RAW_SNIPPET_CHARS {
259 snippet.push('…');
260 }
261 snippet
262}
263
264fn string_field(value: &Value, field: &str) -> String {
265 value
266 .get(field)
267 .and_then(Value::as_str)
268 .unwrap_or_default()
269 .to_owned()
270}
271
272fn optional_number_string(value: &Value, field: &str) -> Option<NumberString> {
273 value.get(field).and_then(Value::as_str).map(Into::into)
274}