1use bytes::Bytes;
2use serde_json::Error as SerdeError;
3
4use crate::model::{
5 CustomSkinProcessUpdate, Event, RenderAdded, RenderDone, RenderFailed, RenderProgress,
6};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum RawEvent {
31 RenderAdded(RawRenderAdded),
32 RenderDone(RawRenderDone),
33 RenderFailed(RawRenderFailed),
34 RenderProgress(RawRenderProgress),
35 CustomSkinProcessUpdate(RawCustomSkinProcessUpdate),
36}
37
38impl RawEvent {
39 pub(crate) fn from_bytes(bytes: Bytes) -> Result<Self, crate::WebsocketError> {
40 fn split_bytes(bytes: &[u8]) -> Option<(&[u8], &[u8])> {
41 let comma_idx = bytes.iter().position(|&byte| byte == b',')?;
42
43 let ([b'[', b'"', prefix @ .., b'"'], [_, suffix @ .., b']']) =
44 bytes.split_at(comma_idx)
45 else {
46 return None;
47 };
48
49 Some((prefix, suffix))
50 }
51
52 fn find_render_id(mut bytes: &[u8]) -> Option<u32> {
53 loop {
54 let idx = bytes.iter().position(|&byte| byte == b'r')?;
55
56 let Some(rest) = bytes[idx..].strip_prefix(b"renderID\":") else {
57 bytes = &bytes[idx + 1..];
58
59 continue;
60 };
61
62 rest.first().copied().filter(u8::is_ascii_digit)?;
63
64 let render_id = rest
65 .iter()
66 .copied()
67 .take_while(u8::is_ascii_digit)
68 .fold(0, |num, byte| num * 10 + u32::from(byte & 0xF));
69
70 return Some(render_id);
71 }
72 }
73
74 let Some((event, payload)) = split_bytes(&bytes) else {
75 return Err(crate::WebsocketError::InvalidEvent(bytes));
76 };
77
78 let payload_bytes = bytes.slice_ref(payload);
79
80 match event {
81 b"render_progress_json" => find_render_id(payload)
82 .map(|render_id| {
83 Self::RenderProgress(RawRenderProgress {
84 render_id,
85 bytes: payload_bytes,
86 })
87 })
88 .ok_or(crate::WebsocketError::InvalidEvent(bytes)),
89 b"render_added_json" => Ok(Self::RenderAdded(RawRenderAdded {
90 bytes: payload_bytes,
91 })),
92 b"render_done_json" => find_render_id(payload)
93 .map(|render_id| {
94 Self::RenderDone(RawRenderDone {
95 render_id,
96 bytes: payload_bytes,
97 })
98 })
99 .ok_or(crate::WebsocketError::InvalidEvent(bytes)),
100 b"render_failed_json" => find_render_id(payload)
101 .map(|render_id| {
102 Self::RenderFailed(RawRenderFailed {
103 render_id,
104 bytes: payload_bytes,
105 })
106 })
107 .ok_or(crate::WebsocketError::InvalidEvent(bytes)),
108 b"custom_skin_process_update" => {
109 Ok(Self::CustomSkinProcessUpdate(RawCustomSkinProcessUpdate {
110 bytes: payload_bytes,
111 }))
112 }
113 _ => Err(crate::WebsocketError::InvalidEvent(bytes)),
114 }
115 }
116
117 pub fn deserialize(&self) -> Result<Event, SerdeError> {
119 match self {
120 RawEvent::RenderAdded(event) => event.deserialize().map(Event::RenderAdded),
121 RawEvent::RenderDone(event) => event.deserialize().map(Event::RenderDone),
122 RawEvent::RenderFailed(event) => event.deserialize().map(Event::RenderFailed),
123 RawEvent::RenderProgress(event) => event.deserialize().map(Event::RenderProgress),
124 RawEvent::CustomSkinProcessUpdate(event) => {
125 event.deserialize().map(Event::CustomSkinProcessUpdate)
126 }
127 }
128 }
129}
130
131#[derive(Clone, Debug, PartialEq, Eq)]
133pub struct RawRenderAdded {
134 pub bytes: Bytes,
135}
136
137impl RawRenderAdded {
138 pub fn deserialize(&self) -> Result<RenderAdded, SerdeError> {
140 serde_json::from_slice(&self.bytes)
141 }
142}
143
144#[derive(Clone, Debug, PartialEq, Eq)]
146pub struct RawRenderProgress {
147 pub render_id: u32,
148 pub bytes: Bytes,
149}
150
151impl RawRenderProgress {
152 pub fn deserialize(&self) -> Result<RenderProgress, SerdeError> {
154 serde_json::from_slice(&self.bytes)
155 }
156}
157
158#[derive(Clone, Debug, PartialEq, Eq)]
160pub struct RawRenderFailed {
161 pub render_id: u32,
162 pub bytes: Bytes,
163}
164
165impl RawRenderFailed {
166 pub fn deserialize(&self) -> Result<RenderFailed, SerdeError> {
168 serde_json::from_slice(&self.bytes)
169 }
170}
171
172#[derive(Clone, Debug, PartialEq, Eq)]
174pub struct RawRenderDone {
175 pub render_id: u32,
176 pub bytes: Bytes,
177}
178
179impl RawRenderDone {
180 pub fn deserialize(&self) -> Result<RenderDone, SerdeError> {
182 serde_json::from_slice(&self.bytes)
183 }
184}
185
186#[derive(Clone, Debug, PartialEq, Eq)]
188pub struct RawCustomSkinProcessUpdate {
189 pub bytes: Bytes,
190}
191
192impl RawCustomSkinProcessUpdate {
193 pub fn deserialize(&self) -> Result<CustomSkinProcessUpdate, SerdeError> {
195 serde_json::from_slice(&self.bytes)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use crate::client::error::ErrorCode;
203
204 #[test]
205 fn parse_progress_event() {
206 let frame = br#"["render_progress_json",{"description":"","progress":"45%","renderID":123,"renderer":"r","username":"u"}]"#;
207
208 let progress = match RawEvent::from_bytes(Bytes::from_static(frame)).unwrap() {
209 RawEvent::RenderProgress(progress) => progress,
210 event => panic!("unexpected event: {event:?}"),
211 };
212
213 assert_eq!(progress.render_id, 123);
214 assert_eq!(progress.deserialize().unwrap().progress.as_ref(), "45%");
215 }
216
217 #[test]
218 fn parse_done_event() {
219 let frame = br#"["render_done_json",{"renderID":1,"videoUrl":"https://ordr.issou.best/video.mp4"}]"#;
220
221 let done = match RawEvent::from_bytes(Bytes::from_static(frame)).unwrap() {
222 RawEvent::RenderDone(done) => done,
223 event => panic!("unexpected event: {event:?}"),
224 };
225
226 assert_eq!(done.render_id, 1);
227 assert_eq!(
228 done.deserialize().unwrap().video_url.as_ref(),
229 "https://ordr.issou.best/video.mp4"
230 );
231 }
232
233 #[test]
234 fn parse_failed_event() {
235 let frame =
236 br#"["render_failed_json",{"renderID":2,"errorCode":2,"errorMessage":"bad replay"}]"#;
237
238 let failed = match RawEvent::from_bytes(Bytes::from_static(frame)).unwrap() {
239 RawEvent::RenderFailed(failed) => failed,
240 event => panic!("unexpected event: {event:?}"),
241 };
242
243 let failed = failed.deserialize().unwrap();
244
245 assert_eq!(failed.render_id, 2);
246 assert_eq!(failed.error_code, Some(ErrorCode::ReplayParsingError));
247 assert_eq!(failed.error_message.as_ref(), "bad replay");
248 }
249
250 #[test]
251 fn parse_added_event() {
252 let frame = br#"["render_added_json",{"renderID":9}]"#;
253
254 let added = match RawEvent::from_bytes(Bytes::from_static(frame)).unwrap() {
255 RawEvent::RenderAdded(added) => added,
256 event => panic!("unexpected event: {event:?}"),
257 };
258
259 assert_eq!(added.deserialize().unwrap().render_id, 9);
260 }
261
262 #[test]
263 fn parse_custom_skin_event() {
264 let frame = br#"["custom_skin_process_update",{"skinId":7}]"#;
265
266 assert!(matches!(
267 RawEvent::from_bytes(Bytes::from_static(frame)).unwrap(),
268 RawEvent::CustomSkinProcessUpdate(_),
269 ));
270 }
271
272 #[test]
273 fn reject_unknown_event() {
274 let frame = br#"["nope_json",{"a":1}]"#;
275
276 assert!(RawEvent::from_bytes(Bytes::from_static(frame)).is_err());
277 }
278
279 #[test]
280 fn reject_event_without_render_id() {
281 let frame = br#"["render_progress_json",{"description":""}]"#;
282
283 assert!(RawEvent::from_bytes(Bytes::from_static(frame)).is_err());
284 }
285}