1use std::collections::HashMap;
2
3use serde::{de, ser, Deserialize, Serialize};
4use serde_json::{json, Value};
5use serde_repr::{Deserialize_repr, Serialize_repr};
6use serde_with::skip_serializing_none;
7
8macro_rules! get_from_map {
9 ($map:expr, $key:expr) => {
10 $map.get($key).ok_or(de::Error::missing_field($key))
11 };
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum MetadataObject {
16 Generic {
17 title: Option<String>,
18 thumbnail_url: Option<String>,
19 custom: Option<Value>,
20 },
21}
22
23impl Serialize for MetadataObject {
24 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
25 where
26 S: serde::Serializer,
27 {
28 match self {
29 MetadataObject::Generic {
30 title,
31 thumbnail_url,
32 custom,
33 } => {
34 let mut map = serde_json::Map::new();
35 map.insert("type".to_owned(), json!(0u64));
36 map.insert(
37 "title".to_owned(),
38 match title {
39 Some(t) => Value::String(t.to_owned()),
40 None => Value::Null,
41 },
42 );
43 map.insert(
44 "thumbnailUrl".to_owned(),
45 match thumbnail_url {
46 Some(t) => Value::String(t.to_owned()),
47 None => Value::Null,
48 },
49 );
50 if let Some(custom) = custom {
51 map.insert("custom".to_owned(), custom.clone());
52 }
53 map.serialize(serializer)
54 }
55 }
56 }
57}
58
59impl<'de> Deserialize<'de> for MetadataObject {
60 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
61 where
62 D: serde::Deserializer<'de>,
63 {
64 let mut map = serde_json::Map::deserialize(deserializer)?;
65
66 let type_ = map
67 .remove("type")
68 .ok_or(de::Error::missing_field("type"))?
69 .as_u64()
70 .ok_or(de::Error::custom("`type` is not an integer"))?;
71 let rest = Value::Object(map);
72
73 match type_ {
74 0 => {
75 let title = match rest.get("title") {
76 None | Some(Value::Null) => None,
77 Some(t) => Some(
78 t.as_str()
79 .ok_or(de::Error::custom("`title` is not a string"))?
80 .to_owned(),
81 ),
82 };
83 let thumbnail_url = match rest.get("thumbnailUrl") {
84 None | Some(Value::Null) => None,
85 Some(t) => Some(
86 t.as_str()
87 .ok_or(de::Error::custom("`thumbnailUrl` is not a string"))?
88 .to_owned(),
89 ),
90 };
91 Ok(Self::Generic {
92 title,
93 thumbnail_url,
94 custom: rest.get("custom").cloned(),
95 })
96 }
97 _ => Err(de::Error::custom(format!("Unknown metadata type {type_}"))),
98 }
99 }
100}
101
102#[skip_serializing_none]
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
104pub struct PlayMessage {
105 pub container: String,
107 pub url: Option<String>,
109 pub content: Option<String>,
111 pub time: Option<f64>,
113 pub volume: Option<f64>,
115 pub speed: Option<f64>,
117 pub headers: Option<HashMap<String, String>>,
119 pub metadata: Option<MetadataObject>,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize_repr, Serialize_repr)]
123#[repr(u8)]
124pub enum ContentType {
125 #[default]
126 Playlist = 0,
127}
128
129#[skip_serializing_none]
130#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
131pub struct MediaItem {
132 pub container: String,
134 pub url: Option<String>,
136 pub content: Option<String>,
138 pub time: Option<f64>,
140 pub volume: Option<f64>,
142 pub speed: Option<f64>,
144 pub cache: Option<bool>,
146 #[serde(rename = "showDuration")]
148 pub show_duration: Option<f64>,
149 pub headers: Option<HashMap<String, String>>,
151 pub metadata: Option<MetadataObject>,
152}
153
154impl From<PlayMessage> for MediaItem {
155 fn from(msg: PlayMessage) -> Self {
156 Self {
157 container: msg.container,
158 url: msg.url,
159 content: msg.content,
160 time: msg.time,
161 volume: msg.volume,
162 speed: msg.speed,
163 headers: msg.headers,
164 metadata: msg.metadata,
165 ..Default::default()
166 }
167 }
168}
169
170#[skip_serializing_none]
171#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
172pub struct PlaylistContent {
173 #[serde(rename = "contentType")]
174 pub variant: ContentType,
175 pub items: Vec<MediaItem>,
176 pub offset: Option<u64>, pub volume: Option<f64>,
180 pub speed: Option<f64>,
182 #[serde(rename = "forwardCache")]
184 pub forward_cache: Option<u64>,
185 #[serde(rename = "backwardCache")]
187 pub backward_cache: Option<u64>,
188 pub metadata: Option<MetadataObject>,
189}
190
191#[skip_serializing_none]
192#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
193pub struct PlaybackUpdateMessage {
194 #[serde(rename = "generationTime")]
196 pub generation_time: u64,
197 pub state: crate::PlaybackState,
199 pub time: Option<f64>,
201 pub duration: Option<f64>,
203 pub speed: Option<f64>,
205 #[serde(rename = "itemIndex")]
207 pub item_index: Option<u64>,
208}
209
210#[skip_serializing_none]
211#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
212pub struct InitialSenderMessage {
213 #[serde(rename = "displayName")]
214 pub display_name: Option<String>,
215 #[serde(rename = "appName")]
216 pub app_name: Option<String>,
217 #[serde(rename = "appVersion")]
218 pub app_version: Option<String>,
219}
220
221#[skip_serializing_none]
222#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
223pub struct LivestreamCapabilities {
224 pub whep: Option<bool>,
226}
227
228#[skip_serializing_none]
229#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
230pub struct AVCapabilities {
231 pub livestream: Option<LivestreamCapabilities>,
232}
233
234#[skip_serializing_none]
235#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
236pub struct ReceiverCapabilities {
237 pub av: Option<AVCapabilities>,
238}
239
240#[skip_serializing_none]
241#[allow(dead_code)]
242#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
243pub struct InitialReceiverMessage {
244 #[serde(rename = "displayName")]
245 pub display_name: Option<String>,
246 #[serde(rename = "appName")]
247 pub app_name: Option<String>,
248 #[serde(rename = "appVersion")]
249 pub app_version: Option<String>,
250 #[serde(rename = "playData")]
251 pub play_data: Option<PlayMessage>,
252 #[serde(rename = "experimentalCapabilities")]
253 pub experimental_capabilities: Option<ReceiverCapabilities>,
254}
255
256#[skip_serializing_none]
257#[allow(dead_code)]
258#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
259pub struct PlayUpdateMessage {
260 #[serde(rename = "generationTime")]
261 pub generation_time: Option<u64>,
262 #[serde(rename = "playData")]
263 pub play_data: Option<PlayMessage>,
264}
265
266#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
267pub struct SetPlaylistItemMessage {
268 #[serde(rename = "itemIndex")]
269 pub item_index: u64,
270}
271
272#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
273pub enum KeyNames {
274 ArrowLeft,
275 ArrowRight,
276 ArrowUp,
277 ArrowDown,
278 Enter,
279}
280
281#[allow(dead_code)]
282impl KeyNames {
283 pub fn all() -> Vec<String> {
284 vec![
285 "ArrowLeft".to_owned(),
286 "ArrowRight".to_owned(),
287 "ArrowUp".to_owned(),
288 "ArrowDown".to_owned(),
289 "Enter".to_owned(),
290 ]
291 }
292}
293
294#[derive(Debug, PartialEq, Eq, Clone, Hash)]
295pub enum EventSubscribeObject {
296 MediaItemStart,
297 MediaItemEnd,
298 MediaItemChanged,
299 KeyDown { keys: Vec<String> },
300 KeyUp { keys: Vec<String> },
301}
302
303impl Serialize for EventSubscribeObject {
304 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
305 where
306 S: serde::Serializer,
307 {
308 let mut map = serde_json::Map::new();
309 let type_val: u64 = match self {
310 EventSubscribeObject::MediaItemStart => 0,
311 EventSubscribeObject::MediaItemEnd => 1,
312 EventSubscribeObject::MediaItemChanged => 2,
313 EventSubscribeObject::KeyDown { .. } => 3,
314 EventSubscribeObject::KeyUp { .. } => 4,
315 };
316
317 map.insert("type".to_owned(), json!(type_val));
318
319 let keys = match self {
320 EventSubscribeObject::KeyDown { keys } => Some(keys),
321 EventSubscribeObject::KeyUp { keys } => Some(keys),
322 _ => None,
323 };
324 if let Some(keys) = keys {
325 map.insert("keys".to_owned(), json!(keys));
326 }
327
328 map.serialize(serializer)
329 }
330}
331
332impl<'de> Deserialize<'de> for EventSubscribeObject {
333 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
334 where
335 D: serde::Deserializer<'de>,
336 {
337 let mut map = serde_json::Map::deserialize(deserializer)?;
338 let type_ = map
339 .remove("type")
340 .ok_or(de::Error::missing_field("type"))?
341 .as_u64()
342 .ok_or(de::Error::custom("`type` is not an integer"))?;
343 let rest = Value::Object(map);
344
345 match type_ {
346 0 => Ok(Self::MediaItemStart),
347 1 => Ok(Self::MediaItemEnd),
348 2 => Ok(Self::MediaItemChanged),
349 3 | 4 => {
350 let keys = get_from_map!(rest, "keys")?
351 .as_array()
352 .ok_or(de::Error::custom("`type` is not an array"))?
353 .iter()
354 .map(|v| v.as_str().map(|s| s.to_owned()))
355 .collect::<Option<Vec<String>>>()
356 .ok_or(de::Error::custom("`type` is not an array of strings"))?;
357 if type_ == 3 {
358 Ok(Self::KeyDown { keys })
359 } else {
360 Ok(Self::KeyUp { keys })
361 }
362 }
363 _ => Err(de::Error::custom(format!("Unknown event type {type_}"))),
364 }
365 }
366}
367
368#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
369pub struct SubscribeEventMessage {
370 pub event: EventSubscribeObject,
371}
372
373#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
374pub struct UnsubscribeEventMessage {
375 pub event: EventSubscribeObject,
376}
377
378#[derive(Debug, PartialEq, Eq, Clone, Copy)]
379#[repr(u8)]
380pub enum EventType {
381 MediaItemStart = 0,
382 MediaItemEnd = 1,
383 MediaItemChange = 2,
384 KeyDown = 3,
385 KeyUp = 4,
386}
387
388#[derive(Debug, PartialEq, Clone)]
389#[allow(clippy::large_enum_variant)]
390pub enum EventObject {
391 MediaItem {
392 variant: EventType,
393 item: MediaItem,
394 },
395 Key {
396 variant: EventType,
397 key: String,
398 repeat: bool,
399 handled: bool,
400 },
401}
402
403impl Serialize for EventObject {
404 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
405 where
406 S: serde::Serializer,
407 {
408 let mut map = serde_json::Map::new();
409
410 match self {
411 EventObject::MediaItem { variant, item } => {
412 map.insert("type".to_owned(), json!(*variant as u8));
413 map.insert(
414 "item".to_owned(),
415 serde_json::to_value(item).map_err(ser::Error::custom)?,
416 );
417 }
418 EventObject::Key {
419 variant,
420 key,
421 repeat,
422 handled,
423 } => {
424 map.insert("type".to_owned(), json!(*variant as u8));
425 map.insert(
426 "key".to_owned(),
427 serde_json::to_value(key).map_err(ser::Error::custom)?,
428 );
429 map.insert(
430 "repeat".to_owned(),
431 serde_json::to_value(repeat).map_err(ser::Error::custom)?,
432 );
433 map.insert(
434 "handled".to_owned(),
435 serde_json::to_value(handled).map_err(ser::Error::custom)?,
436 );
437 }
438 }
439
440 map.serialize(serializer)
441 }
442}
443
444impl<'de> Deserialize<'de> for EventObject {
445 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
446 where
447 D: de::Deserializer<'de>,
448 {
449 let mut map = serde_json::Map::deserialize(deserializer)?;
450 let type_ = map
451 .remove("type")
452 .ok_or(de::Error::missing_field("type"))?
453 .as_u64()
454 .ok_or(de::Error::custom("`type` is not an integer"))?;
455 let rest = Value::Object(map);
456
457 match type_ {
458 #[allow(clippy::manual_range_patterns)]
459 0 | 1 | 2 => {
460 let variant = match type_ {
461 0 => EventType::MediaItemStart,
462 1 => EventType::MediaItemEnd,
463 _ => EventType::MediaItemChange,
464 };
465 let item = get_from_map!(rest, "item")?;
466 Ok(Self::MediaItem {
467 variant,
468 item: MediaItem::deserialize(item).map_err(de::Error::custom)?,
469 })
470 }
471 3 | 4 => {
472 let variant = if type_ == 3 {
473 EventType::KeyDown
474 } else {
475 EventType::KeyUp
476 };
477 Ok(Self::Key {
478 variant,
479 key: get_from_map!(rest, "key")?
480 .as_str()
481 .ok_or(de::Error::custom("`key` is not a string"))?
482 .to_owned(),
483 repeat: get_from_map!(rest, "repeat")?
484 .as_bool()
485 .ok_or(de::Error::custom("`repeat` is not a bool"))?,
486 handled: get_from_map!(rest, "handled")?
487 .as_bool()
488 .ok_or(de::Error::custom("`handled` is not a bool"))?,
489 })
490 }
491 _ => Err(de::Error::custom(format!("Unknown event type {type_}"))),
492 }
493 }
494}
495
496#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
497pub struct EventMessage {
498 #[serde(rename = "generationTime")]
499 pub generation_time: u64,
500 pub event: EventObject,
501}
502
503pub use crate::v2::VolumeUpdateMessage;
504
505#[cfg(test)]
506mod tests {
507 use super::*;
508
509 macro_rules! s {
510 ($s:expr) => {
511 ($s).to_string()
512 };
513 }
514
515 #[test]
516 fn serialize_metadata_object() {
517 assert_eq!(
518 &serde_json::to_string(&MetadataObject::Generic {
519 title: Some(s!("abc")),
520 thumbnail_url: Some(s!("def")),
521 custom: Some(serde_json::Value::Null),
522 })
523 .unwrap(),
524 r#"{"custom":null,"thumbnailUrl":"def","title":"abc","type":0}"#
525 );
526 assert_eq!(
527 &serde_json::to_string(&MetadataObject::Generic {
528 title: None,
529 thumbnail_url: None,
530 custom: Some(serde_json::Value::Null),
531 })
532 .unwrap(),
533 r#"{"custom":null,"thumbnailUrl":null,"title":null,"type":0}"#
534 );
535 assert_eq!(
536 &serde_json::to_string(&MetadataObject::Generic {
537 title: Some(s!("abc")),
538 thumbnail_url: Some(s!("def")),
539 custom: None,
540 })
541 .unwrap(),
542 r#"{"thumbnailUrl":"def","title":"abc","type":0}"#
543 );
544 assert_eq!(
545 &serde_json::to_string(&MetadataObject::Generic {
546 title: None,
547 thumbnail_url: None,
548 custom: None,
549 })
550 .unwrap(),
551 r#"{"thumbnailUrl":null,"title":null,"type":0}"#
552 );
553 }
554
555 #[test]
556 fn deserialize_metadata_object() {
557 assert_eq!(
558 serde_json::from_str::<MetadataObject>(
559 r#"{"type":0,"title":"abc","thumbnailUrl":"def","custom":null}"#
560 )
561 .unwrap(),
562 MetadataObject::Generic {
563 title: Some(s!("abc")),
564 thumbnail_url: Some(s!("def")),
565 custom: Some(serde_json::Value::Null),
566 }
567 );
568 assert_eq!(
569 serde_json::from_str::<MetadataObject>(r#"{"type":0,"custom":null}"#).unwrap(),
570 MetadataObject::Generic {
571 title: None,
572 thumbnail_url: None,
573 custom: Some(serde_json::Value::Null),
574 }
575 );
576 assert_eq!(
577 serde_json::from_str::<MetadataObject>(r#"{"type":0}"#).unwrap(),
578 MetadataObject::Generic {
579 title: None,
580 thumbnail_url: None,
581 custom: None,
582 }
583 );
584 assert!(serde_json::from_str::<MetadataObject>(r#"{"type":1"#).is_err());
585 assert_eq!(
586 serde_json::from_str::<MetadataObject>(
587 r#"{"thumbnailUrl":null,"title":null,"type":0}"#
588 )
589 .unwrap(),
590 MetadataObject::Generic {
591 title: None,
592 thumbnail_url: None,
593 custom: None,
594 }
595 );
596 }
597
598 #[test]
599 fn serialize_event_sub_obj() {
600 assert_eq!(
601 &serde_json::to_string(&EventSubscribeObject::MediaItemStart).unwrap(),
602 r#"{"type":0}"#
603 );
604 assert_eq!(
605 &serde_json::to_string(&EventSubscribeObject::MediaItemEnd).unwrap(),
606 r#"{"type":1}"#
607 );
608 assert_eq!(
609 &serde_json::to_string(&EventSubscribeObject::MediaItemChanged).unwrap(),
610 r#"{"type":2}"#
611 );
612 assert_eq!(
613 &serde_json::to_string(&EventSubscribeObject::KeyDown { keys: vec![] }).unwrap(),
614 r#"{"keys":[],"type":3}"#
615 );
616 assert_eq!(
617 &serde_json::to_string(&EventSubscribeObject::KeyDown { keys: vec![] }).unwrap(),
618 r#"{"keys":[],"type":3}"#
619 );
620 assert_eq!(
621 &serde_json::to_string(&EventSubscribeObject::KeyUp {
622 keys: vec![s!("abc"), s!("def")]
623 })
624 .unwrap(),
625 r#"{"keys":["abc","def"],"type":4}"#
626 );
627 assert_eq!(
628 &serde_json::to_string(&EventSubscribeObject::KeyDown {
629 keys: vec![s!("abc"), s!("def")]
630 })
631 .unwrap(),
632 r#"{"keys":["abc","def"],"type":3}"#
633 );
634 assert_eq!(
635 &serde_json::to_string(&EventSubscribeObject::KeyDown {
636 keys: vec![s!("\"\"")]
637 })
638 .unwrap(),
639 r#"{"keys":["\"\""],"type":3}"#
640 );
641 }
642
643 #[test]
644 fn deserialize_event_sub_obj() {
645 assert_eq!(
646 serde_json::from_str::<EventSubscribeObject>(r#"{"type":0}"#).unwrap(),
647 EventSubscribeObject::MediaItemStart
648 );
649 assert_eq!(
650 serde_json::from_str::<EventSubscribeObject>(r#"{"type":1}"#).unwrap(),
651 EventSubscribeObject::MediaItemEnd
652 );
653 assert_eq!(
654 serde_json::from_str::<EventSubscribeObject>(r#"{"type":2}"#).unwrap(),
655 EventSubscribeObject::MediaItemChanged
656 );
657 assert_eq!(
658 serde_json::from_str::<EventSubscribeObject>(r#"{"keys":[],"type":3}"#).unwrap(),
659 EventSubscribeObject::KeyDown { keys: vec![] }
660 );
661 assert_eq!(
662 serde_json::from_str::<EventSubscribeObject>(r#"{"keys":[],"type":4}"#).unwrap(),
663 EventSubscribeObject::KeyUp { keys: vec![] }
664 );
665 assert_eq!(
666 serde_json::from_str::<EventSubscribeObject>(r#"{"keys":["abc","def"],"type":3}"#)
667 .unwrap(),
668 EventSubscribeObject::KeyDown {
669 keys: vec![s!("abc"), s!("def")]
670 }
671 );
672 assert_eq!(
673 serde_json::from_str::<EventSubscribeObject>(r#"{"keys":["abc","def"],"type":4}"#)
674 .unwrap(),
675 EventSubscribeObject::KeyUp {
676 keys: vec![s!("abc"), s!("def")]
677 }
678 );
679 assert!(serde_json::from_str::<EventSubscribeObject>(r#""type":5}"#).is_err());
680 }
681
682 const EMPTY_TEST_MEDIA_ITEM: MediaItem = MediaItem {
683 container: String::new(),
684 url: None,
685 content: None,
686 time: None,
687 volume: None,
688 speed: None,
689 cache: None,
690 show_duration: None,
691 headers: None,
692 metadata: None,
693 };
694 const TEST_MEDIA_ITEM_JSON: &str = r#"{"container":""}"#;
695
696 #[test]
697 fn serialize_event_obj() {
698 assert_eq!(
699 serde_json::to_string(&EventObject::MediaItem {
700 variant: EventType::MediaItemStart,
701 item: EMPTY_TEST_MEDIA_ITEM.clone(),
702 })
703 .unwrap(),
704 format!(r#"{{"item":{TEST_MEDIA_ITEM_JSON},"type":0}}"#),
705 );
706 assert_eq!(
707 serde_json::to_string(&EventObject::MediaItem {
708 variant: EventType::MediaItemEnd,
709 item: EMPTY_TEST_MEDIA_ITEM.clone(),
710 })
711 .unwrap(),
712 format!(r#"{{"item":{TEST_MEDIA_ITEM_JSON},"type":1}}"#),
713 );
714 assert_eq!(
715 serde_json::to_string(&EventObject::MediaItem {
716 variant: EventType::MediaItemChange,
717 item: EMPTY_TEST_MEDIA_ITEM.clone(),
718 })
719 .unwrap(),
720 format!(r#"{{"item":{TEST_MEDIA_ITEM_JSON},"type":2}}"#),
721 );
722 assert_eq!(
723 &serde_json::to_string(&EventObject::Key {
724 variant: EventType::KeyDown,
725 key: s!(""),
726 repeat: false,
727 handled: false,
728 })
729 .unwrap(),
730 r#"{"handled":false,"key":"","repeat":false,"type":3}"#
731 );
732 assert_eq!(
733 &serde_json::to_string(&EventObject::Key {
734 variant: EventType::KeyUp,
735 key: s!(""),
736 repeat: false,
737 handled: false,
738 })
739 .unwrap(),
740 r#"{"handled":false,"key":"","repeat":false,"type":4}"#
741 );
742 }
743
744 #[test]
745 fn deserialize_event_obj() {
746 assert_eq!(
747 serde_json::from_str::<EventObject>(&format!(
748 r#"{{"item":{TEST_MEDIA_ITEM_JSON},"type":0}}"#
749 ))
750 .unwrap(),
751 EventObject::MediaItem {
752 variant: EventType::MediaItemStart,
753 item: EMPTY_TEST_MEDIA_ITEM.clone(),
754 }
755 );
756 assert_eq!(
757 serde_json::from_str::<EventObject>(&format!(
758 r#"{{"item":{TEST_MEDIA_ITEM_JSON},"type":1}}"#
759 ))
760 .unwrap(),
761 EventObject::MediaItem {
762 variant: EventType::MediaItemEnd,
763 item: EMPTY_TEST_MEDIA_ITEM.clone(),
764 }
765 );
766 assert_eq!(
767 serde_json::from_str::<EventObject>(&format!(
768 r#"{{"item":{TEST_MEDIA_ITEM_JSON},"type":2}}"#
769 ))
770 .unwrap(),
771 EventObject::MediaItem {
772 variant: EventType::MediaItemChange,
773 item: EMPTY_TEST_MEDIA_ITEM.clone(),
774 }
775 );
776 assert_eq!(
777 serde_json::from_str::<EventObject>(
778 r#"{"handled":false,"key":"","repeat":false,"type":3}"#
779 )
780 .unwrap(),
781 EventObject::Key {
782 variant: EventType::KeyDown,
783 key: s!(""),
784 repeat: false,
785 handled: false,
786 }
787 );
788 assert_eq!(
789 serde_json::from_str::<EventObject>(
790 r#"{"handled":false,"key":"","repeat":false,"type":4}"#
791 )
792 .unwrap(),
793 EventObject::Key {
794 variant: EventType::KeyUp,
795 key: s!(""),
796 repeat: false,
797 handled: false,
798 }
799 );
800 assert!(serde_json::from_str::<EventObject>(r#"{"type":5}"#).is_err());
801 }
802
803 #[test]
804 fn serialize_playlist_content() {
805 assert_eq!(
806 serde_json::to_string(&PlaylistContent {
807 variant: ContentType::Playlist,
808 items: Vec::new(),
809 offset: None,
810 volume: None,
811 speed: None,
812 forward_cache: None,
813 backward_cache: None,
814 metadata: None
815 })
816 .unwrap(),
817 r#"{"contentType":0,"items":[]}"#,
818 );
819 assert_eq!(
820 serde_json::to_string(&PlaylistContent {
821 variant: ContentType::Playlist,
822 items: Vec::new(),
823 offset: None,
824 volume: Some(1.0),
825 speed: Some(1.0),
826 forward_cache: None,
827 backward_cache: None,
828 metadata: None
829 })
830 .unwrap(),
831 r#"{"contentType":0,"items":[],"volume":1.0,"speed":1.0}"#,
832 );
833 assert_eq!(
834 serde_json::to_string(&PlaylistContent {
835 variant: ContentType::Playlist,
836 items: vec![MediaItem {
837 container: "video/mp4".to_string(),
838 url: Some("abc".to_string()),
839 content: None,
840 time: None,
841 volume: None,
842 speed: None,
843 cache: None,
844 show_duration: None,
845 headers: None,
846 metadata: None
847 }],
848 offset: None,
849 volume: None,
850 speed: None,
851 forward_cache: None,
852 backward_cache: None,
853 metadata: None
854 })
855 .unwrap(),
856 r#"{"contentType":0,"items":[{"container":"video/mp4","url":"abc"}]}"#,
857 );
858 assert_eq!(
859 serde_json::to_string(&PlaylistContent {
860 variant: ContentType::Playlist,
861 items: vec![MediaItem {
862 container: "video/mp4".to_string(),
863 url: Some("abc".to_string()),
864 content: None,
865 time: None,
866 volume: None,
867 speed: None,
868 cache: None,
869 show_duration: None,
870 headers: None,
871 metadata: None
872 }],
873 offset: None,
874 volume: Some(1.0),
875 speed: Some(1.0),
876 forward_cache: None,
877 backward_cache: None,
878 metadata: Some(MetadataObject::Generic {
879 title: None,
880 thumbnail_url: None,
881 custom: None
882 })
883 })
884 .unwrap(),
885 r#"{"contentType":0,"items":[{"container":"video/mp4","url":"abc"}],"volume":1.0,"speed":1.0,"metadata":{"thumbnailUrl":null,"title":null,"type":0}}"#,
886 );
887 }
888}