1use o_sfu_model::RecordingOptions;
2use serde::{Serialize, de::DeserializeOwned};
3use serde_json::Value;
4
5use super::{
6 AuthPayload, ClientBroadcastPayload, Envelope, PeerInfoPayload, PeerLeftPayload,
7 RecordingActionResult, RequestId, ServerBroadcastPayload, SessionDescriptionPayload,
8 SourceDescriptor, StreamIntentPayload, SubscribePayload, TrackBinding, WelcomePayload,
9};
10use crate::shared::{RecordingStateUpdate, UserInfo};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13enum WireTag {
14 Auth,
15 Broadcast,
16 Info,
17 Offer,
18 PeerInfo,
19 PeerJoined,
20 PeerLeft,
21 Publish,
22 RecordingChange,
23 Renegotiate,
24 StartRecording,
25 StopRecording,
26 Subscribe,
27 Sources,
28 Tracks,
29 Unpublish,
30 Welcome,
31}
32
33impl WireTag {
34 const fn as_str(self) -> &'static str {
35 match self {
36 Self::Auth => "auth",
37 Self::Broadcast => "broadcast",
38 Self::Info => "info",
39 Self::Offer => "offer",
40 Self::PeerInfo => "peerinfo",
41 Self::PeerJoined => "peerjoined",
42 Self::PeerLeft => "peerleft",
43 Self::Publish => "publish",
44 Self::RecordingChange => "recordingchange",
45 Self::Renegotiate => "renegotiate",
46 Self::StartRecording => "startrecording",
47 Self::StopRecording => "stoprecording",
48 Self::Subscribe => "subscribe",
49 Self::Sources => "sources",
50 Self::Tracks => "tracks",
51 Self::Unpublish => "unpublish",
52 Self::Welcome => "welcome",
53 }
54 }
55}
56
57type EntryDecode<T> = fn(WireTag, Option<Value>) -> Result<T, EnvelopeDecodeError>;
58
59#[derive(Clone, Copy)]
60struct EnvelopeEntry<T> {
61 tag: WireTag,
62 decode: EntryDecode<T>,
63}
64
65impl<T> EnvelopeEntry<T> {
66 const fn new(tag: WireTag, decode: EntryDecode<T>) -> Self {
67 Self { tag, decode }
68 }
69
70 fn decode(&self, payload: Option<Value>) -> Result<T, EnvelopeDecodeError> {
71 (self.decode)(self.tag, payload)
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum EnvelopeDecodeError {
77 UnknownTag(String),
78 InvalidPayload(String),
79 UnexpectedPayload(String),
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub enum ClientMessage {
84 Auth(AuthPayload),
85 Publish(StreamIntentPayload),
86 Unpublish(StreamIntentPayload),
87 Subscribe(SubscribePayload),
88 Info(UserInfo),
89 Broadcast(ClientBroadcastPayload),
90}
91
92impl ClientMessage {
93 const ENTRIES: &'static [EnvelopeEntry<Self>] = &[
94 EnvelopeEntry::new(WireTag::Auth, |tag, payload| {
95 decode_payload(tag, payload, Self::Auth)
96 }),
97 EnvelopeEntry::new(WireTag::Publish, |tag, payload| {
98 decode_payload(tag, payload, Self::Publish)
99 }),
100 EnvelopeEntry::new(WireTag::Unpublish, |tag, payload| {
101 decode_payload(tag, payload, Self::Unpublish)
102 }),
103 EnvelopeEntry::new(WireTag::Subscribe, |tag, payload| {
104 decode_payload(tag, payload, Self::Subscribe)
105 }),
106 EnvelopeEntry::new(WireTag::Info, |tag, payload| {
107 decode_payload(tag, payload, Self::Info)
108 }),
109 EnvelopeEntry::new(WireTag::Broadcast, |tag, payload| {
110 decode_payload(tag, payload, Self::Broadcast)
111 }),
112 ];
113
114 pub(crate) fn into_envelope(self) -> Result<Envelope, serde_json::Error> {
115 match self {
116 Self::Auth(payload) => encode_message(WireTag::Auth, payload),
117 Self::Publish(payload) => encode_message(WireTag::Publish, payload),
118 Self::Unpublish(payload) => encode_message(WireTag::Unpublish, payload),
119 Self::Subscribe(payload) => encode_message(WireTag::Subscribe, payload),
120 Self::Info(payload) => encode_message(WireTag::Info, payload),
121 Self::Broadcast(payload) => encode_message(WireTag::Broadcast, payload),
122 }
123 }
124
125 pub(crate) fn decode(tag: &str, payload: Option<Value>) -> Result<Self, EnvelopeDecodeError> {
126 decode_entry(tag, payload, Self::ENTRIES)
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub enum ClientRequest {
132 StartRecording(RecordingOptions),
133 StopRecording,
134}
135
136impl ClientRequest {
137 const ENTRIES: &'static [EnvelopeEntry<Self>] = &[
138 EnvelopeEntry::new(WireTag::StartRecording, |tag, payload| {
139 decode_payload(tag, payload, Self::StartRecording)
140 }),
141 EnvelopeEntry::new(WireTag::StopRecording, |tag, payload| {
142 decode_empty(tag, payload.as_ref(), Self::StopRecording)
143 }),
144 ];
145
146 pub(crate) fn into_envelope(
147 self,
148 request_id: RequestId,
149 ) -> Result<Envelope, serde_json::Error> {
150 match self {
151 Self::StartRecording(payload) => {
152 encode_request(WireTag::StartRecording, request_id, payload)
153 }
154 Self::StopRecording => Ok(Envelope::request(
155 WireTag::StopRecording.as_str(),
156 request_id,
157 None,
158 )),
159 }
160 }
161
162 pub(crate) fn decode(tag: &str, payload: Option<Value>) -> Result<Self, EnvelopeDecodeError> {
163 decode_entry(tag, payload, Self::ENTRIES)
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
168pub enum ServerRequest {
169 Offer(SessionDescriptionPayload),
170 Renegotiate(SessionDescriptionPayload),
171}
172
173impl ServerRequest {
174 const ENTRIES: &'static [EnvelopeEntry<Self>] = &[
175 EnvelopeEntry::new(WireTag::Offer, |tag, payload| {
176 decode_payload(tag, payload, Self::Offer)
177 }),
178 EnvelopeEntry::new(WireTag::Renegotiate, |tag, payload| {
179 decode_payload(tag, payload, Self::Renegotiate)
180 }),
181 ];
182
183 pub fn into_envelope(self, request_id: RequestId) -> Result<Envelope, serde_json::Error> {
190 match self {
191 Self::Offer(payload) => encode_request(WireTag::Offer, request_id, payload),
192 Self::Renegotiate(payload) => encode_request(WireTag::Renegotiate, request_id, payload),
193 }
194 }
195
196 pub(crate) fn decode(tag: &str, payload: Option<Value>) -> Result<Self, EnvelopeDecodeError> {
197 decode_entry(tag, payload, Self::ENTRIES)
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub enum ClientResponse {
203 Offer(SessionDescriptionPayload),
204 Renegotiate(SessionDescriptionPayload),
205}
206
207impl ClientResponse {
208 const ENTRIES: &'static [EnvelopeEntry<Self>] = &[
209 EnvelopeEntry::new(WireTag::Offer, |tag, payload| {
210 decode_payload(tag, payload, Self::Offer)
211 }),
212 EnvelopeEntry::new(WireTag::Renegotiate, |tag, payload| {
213 decode_payload(tag, payload, Self::Renegotiate)
214 }),
215 ];
216
217 pub(crate) fn into_envelope(
218 self,
219 response_to: RequestId,
220 ) -> Result<Envelope, serde_json::Error> {
221 match self {
222 Self::Offer(payload) => encode_response(WireTag::Offer, response_to, payload),
223 Self::Renegotiate(payload) => {
224 encode_response(WireTag::Renegotiate, response_to, payload)
225 }
226 }
227 }
228
229 pub(crate) fn decode(tag: &str, payload: Option<Value>) -> Result<Self, EnvelopeDecodeError> {
230 decode_entry(tag, payload, Self::ENTRIES)
231 }
232}
233
234#[derive(Debug, Clone, PartialEq, Eq)]
235pub enum ServerMessage {
236 Welcome(WelcomePayload),
237 Tracks(Vec<TrackBinding>),
238 Sources(Vec<SourceDescriptor>),
239 PeerInfo(PeerInfoPayload),
240 PeerJoined(PeerInfoPayload),
241 PeerLeft(PeerLeftPayload),
242 Broadcast(ServerBroadcastPayload),
243 RecordingChange(RecordingStateUpdate),
244}
245
246impl ServerMessage {
247 const ENTRIES: &'static [EnvelopeEntry<Self>] = &[
248 EnvelopeEntry::new(WireTag::Welcome, |tag, payload| {
249 decode_payload(tag, payload, Self::Welcome)
250 }),
251 EnvelopeEntry::new(WireTag::Tracks, |tag, payload| {
252 decode_payload(tag, payload, Self::Tracks)
253 }),
254 EnvelopeEntry::new(WireTag::Sources, |tag, payload| {
255 decode_payload(tag, payload, Self::Sources)
256 }),
257 EnvelopeEntry::new(WireTag::PeerInfo, |tag, payload| {
258 decode_payload(tag, payload, Self::PeerInfo)
259 }),
260 EnvelopeEntry::new(WireTag::PeerJoined, |tag, payload| {
261 decode_payload(tag, payload, Self::PeerJoined)
262 }),
263 EnvelopeEntry::new(WireTag::PeerLeft, |tag, payload| {
264 decode_payload(tag, payload, Self::PeerLeft)
265 }),
266 EnvelopeEntry::new(WireTag::Broadcast, |tag, payload| {
267 decode_payload(tag, payload, Self::Broadcast)
268 }),
269 EnvelopeEntry::new(WireTag::RecordingChange, |tag, payload| {
270 decode_payload(tag, payload, Self::RecordingChange)
271 }),
272 ];
273
274 pub fn into_envelope(self) -> Result<Envelope, serde_json::Error> {
281 match self {
282 Self::Welcome(payload) => encode_message(WireTag::Welcome, payload),
283 Self::Tracks(payload) => encode_message(WireTag::Tracks, payload),
284 Self::Sources(payload) => encode_message(WireTag::Sources, payload),
285 Self::PeerInfo(payload) => encode_message(WireTag::PeerInfo, payload),
286 Self::PeerJoined(payload) => encode_message(WireTag::PeerJoined, payload),
287 Self::PeerLeft(payload) => encode_message(WireTag::PeerLeft, payload),
288 Self::Broadcast(payload) => encode_message(WireTag::Broadcast, payload),
289 Self::RecordingChange(payload) => encode_message(WireTag::RecordingChange, payload),
290 }
291 }
292
293 pub(crate) fn decode(tag: &str, payload: Option<Value>) -> Result<Self, EnvelopeDecodeError> {
294 decode_entry(tag, payload, Self::ENTRIES)
295 }
296}
297
298#[derive(Debug, Clone, PartialEq, Eq)]
299pub enum ServerResponse {
300 StartRecording(RecordingActionResult),
301 StopRecording(RecordingActionResult),
302}
303
304impl ServerResponse {
305 const ENTRIES: &'static [EnvelopeEntry<Self>] = &[
306 EnvelopeEntry::new(WireTag::StartRecording, |tag, payload| {
307 decode_payload(tag, payload, Self::StartRecording)
308 }),
309 EnvelopeEntry::new(WireTag::StopRecording, |tag, payload| {
310 decode_payload(tag, payload, Self::StopRecording)
311 }),
312 ];
313
314 pub fn into_envelope(self, response_to: RequestId) -> Result<Envelope, serde_json::Error> {
321 match self {
322 Self::StartRecording(payload) => {
323 encode_response(WireTag::StartRecording, response_to, payload)
324 }
325 Self::StopRecording(payload) => {
326 encode_response(WireTag::StopRecording, response_to, payload)
327 }
328 }
329 }
330
331 pub(crate) fn decode(tag: &str, payload: Option<Value>) -> Result<Self, EnvelopeDecodeError> {
332 decode_entry(tag, payload, Self::ENTRIES)
333 }
334}
335
336fn encode_message<T: Serialize>(tag: WireTag, payload: T) -> Result<Envelope, serde_json::Error> {
337 Ok(Envelope::message(
338 tag.as_str(),
339 Some(serde_json::to_value(payload)?),
340 ))
341}
342
343fn encode_request<T: Serialize>(
344 tag: WireTag,
345 request_id: RequestId,
346 payload: T,
347) -> Result<Envelope, serde_json::Error> {
348 Ok(Envelope::request(
349 tag.as_str(),
350 request_id,
351 Some(serde_json::to_value(payload)?),
352 ))
353}
354
355fn encode_response<T: Serialize>(
356 tag: WireTag,
357 response_to: RequestId,
358 payload: T,
359) -> Result<Envelope, serde_json::Error> {
360 Ok(Envelope::response(
361 tag.as_str(),
362 response_to,
363 Some(serde_json::to_value(payload)?),
364 ))
365}
366
367fn decode_entry<T>(
368 tag: &str,
369 payload: Option<Value>,
370 entries: &[EnvelopeEntry<T>],
371) -> Result<T, EnvelopeDecodeError> {
372 entries
373 .iter()
374 .find(|entry| entry.tag.as_str() == tag)
375 .ok_or_else(|| unknown_tag(tag))?
376 .decode(payload)
377}
378
379fn decode_payload<T, P>(
380 tag: WireTag,
381 payload: Option<Value>,
382 build: fn(P) -> T,
383) -> Result<T, EnvelopeDecodeError>
384where
385 P: DeserializeOwned,
386{
387 parse_payload(tag.as_str(), payload).map(build)
388}
389
390fn decode_empty<T>(
391 tag: WireTag,
392 payload: Option<&Value>,
393 value: T,
394) -> Result<T, EnvelopeDecodeError> {
395 ensure_empty_payload(tag.as_str(), payload)?;
396 Ok(value)
397}
398
399fn unknown_tag(tag: &str) -> EnvelopeDecodeError {
400 EnvelopeDecodeError::UnknownTag(tag.to_owned())
401}
402
403fn parse_payload<T: DeserializeOwned>(
404 tag: &str,
405 payload: Option<Value>,
406) -> Result<T, EnvelopeDecodeError> {
407 serde_json::from_value(
408 payload.ok_or_else(|| EnvelopeDecodeError::InvalidPayload(tag.to_owned()))?,
409 )
410 .map_err(|_error| EnvelopeDecodeError::InvalidPayload(tag.to_owned()))
411}
412
413fn ensure_empty_payload(tag: &str, payload: Option<&Value>) -> Result<(), EnvelopeDecodeError> {
414 if payload.is_some() {
415 return Err(EnvelopeDecodeError::UnexpectedPayload(tag.to_owned()));
416 }
417 Ok(())
418}