rosu_render/websocket/
event.rs

1use bytes::Bytes;
2use serde_json::Error as SerdeError;
3
4use crate::model::{
5    CustomSkinProcessUpdate, Event, RenderAdded, RenderDone, RenderFailed, RenderProgress,
6};
7
8/// Websocket [`Event`](crate::model::Event) that has not been fully deserialized yet.
9/// This lets you check if you're interested in the event and only then deserialize it.
10///
11/// # Example
12/// ```rust
13/// use std::collections::HashSet;
14/// use rosu_render::websocket::event::RawEvent;
15///
16/// fn handle_event(event: RawEvent, interesting_render_ids: &HashSet<u32>) {
17///     match event {
18///         RawEvent::RenderDone(event) if interesting_render_ids.contains(&event.render_id) => {
19///             match event.deserialize() {
20///                 Ok(done) => println!("{done:?}"),
21///                 Err(err) => println!("Failed to deserialize {event:?} {err:?}"),
22///             }
23///         }
24///         _ => {} // Ignore
25///     }
26/// }
27/// ```
28#[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    /// Deserialize into an [`Event`].
118    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/// [`RenderAdded`](crate::model::RenderAdded) that has not been fully deserialized yet.
132#[derive(Clone, Debug, PartialEq, Eq)]
133pub struct RawRenderAdded {
134    pub bytes: Bytes,
135}
136
137impl RawRenderAdded {
138    /// Deserialize into a [`RenderAdded`] event.
139    pub fn deserialize(&self) -> Result<RenderAdded, SerdeError> {
140        serde_json::from_slice(&self.bytes)
141    }
142}
143
144/// [`RenderProgress`](crate::model::RenderProgress) that has not been fully deserialized yet.
145#[derive(Clone, Debug, PartialEq, Eq)]
146pub struct RawRenderProgress {
147    pub render_id: u32,
148    pub bytes: Bytes,
149}
150
151impl RawRenderProgress {
152    /// Deserialize into a [`RenderProgress`] event.
153    pub fn deserialize(&self) -> Result<RenderProgress, SerdeError> {
154        serde_json::from_slice(&self.bytes)
155    }
156}
157
158/// [`RenderFailed`](crate::model::RenderFailed) that has not been fully deserialized yet.
159#[derive(Clone, Debug, PartialEq, Eq)]
160pub struct RawRenderFailed {
161    pub render_id: u32,
162    pub bytes: Bytes,
163}
164
165impl RawRenderFailed {
166    /// Deserialize into a [`RenderFailed`] event.
167    pub fn deserialize(&self) -> Result<RenderFailed, SerdeError> {
168        serde_json::from_slice(&self.bytes)
169    }
170}
171
172/// [`RenderDone`](crate::model::RenderDone) that has not been fully deserialized yet.
173#[derive(Clone, Debug, PartialEq, Eq)]
174pub struct RawRenderDone {
175    pub render_id: u32,
176    pub bytes: Bytes,
177}
178
179impl RawRenderDone {
180    /// Deserialize into a [`RenderDone`] event.
181    pub fn deserialize(&self) -> Result<RenderDone, SerdeError> {
182        serde_json::from_slice(&self.bytes)
183    }
184}
185
186/// [`CustomSkinProcessUpdate`](crate::model::CustomSkinProcessUpdate) that has not been fully deserialized yet.
187#[derive(Clone, Debug, PartialEq, Eq)]
188pub struct RawCustomSkinProcessUpdate {
189    pub bytes: Bytes,
190}
191
192impl RawCustomSkinProcessUpdate {
193    /// Deserialize into a [`CustomSkinProcessUpdate`] event.
194    pub fn deserialize(&self) -> Result<CustomSkinProcessUpdate, SerdeError> {
195        serde_json::from_slice(&self.bytes)
196    }
197}