gstreamer/
toc_serde.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use serde::{
4    de::{Deserialize, Deserializer},
5    ser::{Serialize, SerializeStruct, Serializer},
6};
7
8use crate::{toc::*, TagList, TocEntryType, TocLoopType, TocScope};
9
10impl Serialize for TocRef {
11    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
12        let mut toc = serializer.serialize_struct("Toc", 3)?;
13        toc.serialize_field("scope", &self.scope())?;
14        toc.serialize_field("tags", &self.tags())?;
15        toc.serialize_field("entries", &self.entries())?;
16        toc.end()
17    }
18}
19
20impl Serialize for Toc {
21    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
22        self.as_ref().serialize(serializer)
23    }
24}
25
26impl Serialize for TocEntryRef {
27    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
28        let mut toc_entry = serializer.serialize_struct("TocEntry", 6)?;
29        toc_entry.serialize_field("entry_type", &self.entry_type())?;
30        toc_entry.serialize_field("uid", &self.uid())?;
31        toc_entry.serialize_field("start_stop", &self.start_stop_times())?;
32        toc_entry.serialize_field("tags", &self.tags())?;
33        toc_entry.serialize_field("loop", &self.loop_())?;
34        toc_entry.serialize_field("sub_entries", &self.sub_entries())?;
35        toc_entry.end()
36    }
37}
38
39impl Serialize for TocEntry {
40    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
41        self.as_ref().serialize(serializer)
42    }
43}
44
45#[derive(serde::Deserialize)]
46struct TocDe {
47    scope: TocScope,
48    tags: Option<TagList>,
49    entries: Vec<TocEntry>,
50}
51
52impl From<TocDe> for Toc {
53    fn from(mut toc_de: TocDe) -> Self {
54        skip_assert_initialized!();
55        let mut toc = Toc::new(toc_de.scope);
56        {
57            let toc = toc.get_mut().unwrap();
58            toc.set_tags(toc_de.tags.take());
59            let entry_iter = toc_de.entries.drain(..);
60            for entry in entry_iter {
61                toc.append_entry(entry);
62            }
63        }
64        toc
65    }
66}
67
68impl<'de> Deserialize<'de> for Toc {
69    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
70        skip_assert_initialized!();
71        TocDe::deserialize(deserializer).map(|toc_de| toc_de.into())
72    }
73}
74
75#[derive(serde::Deserialize)]
76struct TocEntryDe {
77    entry_type: TocEntryType,
78    uid: String,
79    start_stop: Option<(i64, i64)>,
80    tags: Option<TagList>,
81    #[serde(rename = "loop")]
82    loop_: Option<(TocLoopType, i32)>,
83    sub_entries: Vec<TocEntry>,
84}
85
86impl From<TocEntryDe> for TocEntry {
87    fn from(mut toc_entry_de: TocEntryDe) -> Self {
88        skip_assert_initialized!();
89        let mut toc_entry = TocEntry::new(toc_entry_de.entry_type, toc_entry_de.uid.as_str());
90        {
91            let toc_entry = toc_entry.get_mut().unwrap();
92            if let Some(start_stop) = toc_entry_de.start_stop.take() {
93                toc_entry.set_start_stop_times(start_stop.0, start_stop.1);
94            }
95            toc_entry.set_tags(toc_entry_de.tags.take());
96            if let Some(loop_) = toc_entry_de.loop_.take() {
97                toc_entry.set_loop(loop_.0, loop_.1);
98            }
99
100            let entry_iter = toc_entry_de.sub_entries.drain(..);
101            for sub_entries in entry_iter {
102                toc_entry.append_sub_entry(sub_entries);
103            }
104        }
105        toc_entry
106    }
107}
108
109impl<'de> Deserialize<'de> for TocEntry {
110    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
111        skip_assert_initialized!();
112        TocEntryDe::deserialize(deserializer).map(|toc_entry_de| toc_entry_de.into())
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use crate::{tags::Title, toc::*, TagList, TagMergeMode, TocEntryType, TocScope};
119
120    #[test]
121    fn test_serialize() {
122        crate::init().unwrap();
123
124        let mut toc = Toc::new(TocScope::Global);
125        {
126            let toc = toc.get_mut().unwrap();
127            let mut tags = TagList::new();
128            tags.get_mut()
129                .unwrap()
130                .add::<Title>(&"toc", TagMergeMode::Append);
131            toc.set_tags(tags);
132
133            let mut toc_edition = TocEntry::new(TocEntryType::Edition, "edition");
134            {
135                let toc_edition = toc_edition.get_mut().unwrap();
136                toc_edition.set_start_stop_times(0, 15);
137
138                let mut toc_chap_1 = TocEntry::new(TocEntryType::Chapter, "chapter1");
139                {
140                    let toc_chap_1 = toc_chap_1.get_mut().unwrap();
141                    toc_chap_1.set_start_stop_times(0, 10);
142                    let mut toc_chap_1_1 = TocEntry::new(TocEntryType::Chapter, "chapter1.1");
143                    {
144                        let toc_chap_1_1 = toc_chap_1_1.get_mut().unwrap();
145                        toc_chap_1_1.set_start_stop_times(0, 4);
146                        let mut tags = TagList::new();
147                        tags.get_mut()
148                            .unwrap()
149                            .add::<Title>(&"chapter 1.1", TagMergeMode::Append);
150                        toc_chap_1_1.set_tags(tags);
151                    }
152                    toc_chap_1.append_sub_entry(toc_chap_1_1);
153
154                    let mut toc_chap_1_2 = TocEntry::new(TocEntryType::Chapter, "chapter1.2");
155                    {
156                        let toc_chap_1_2 = toc_chap_1_2.get_mut().unwrap();
157                        toc_chap_1_2.set_start_stop_times(4, 10);
158                        let mut tags = TagList::new();
159                        tags.get_mut()
160                            .unwrap()
161                            .add::<Title>(&"chapter 1.2", TagMergeMode::Append);
162                        toc_chap_1_2.set_tags(tags);
163                    }
164                    toc_chap_1.append_sub_entry(toc_chap_1_2);
165                }
166                toc_edition.append_sub_entry(toc_chap_1);
167
168                let mut toc_chap_2 = TocEntry::new(TocEntryType::Chapter, "chapter2");
169                {
170                    let toc_chap_2 = toc_chap_2.get_mut().unwrap();
171                    toc_chap_2.set_start_stop_times(10, 15);
172                    let mut tags = TagList::new();
173                    tags.get_mut()
174                        .unwrap()
175                        .add::<Title>(&"chapter 2", TagMergeMode::Append);
176                    toc_chap_2.set_tags(tags);
177                }
178                toc_edition.append_sub_entry(toc_chap_2);
179            }
180            toc.append_entry(toc_edition);
181        }
182
183        let pretty_config = ron::ser::PrettyConfig::new().new_line("".to_string());
184
185        let res = ron::ser::to_string_pretty(&toc, pretty_config);
186        assert_eq!(
187            Ok(concat!(
188                "(",
189                "    scope: Global,",
190                "    tags: Some((",
191                "        scope: Stream,",
192                "        tags: [",
193                "            (\"title\", [",
194                "                \"toc\",",
195                "            ]),",
196                "        ],",
197                "    )),",
198                "    entries: [",
199                "        (",
200                "            entry_type: Edition,",
201                "            uid: \"edition\",",
202                "            start_stop: Some((0, 15)),",
203                "            tags: None,",
204                "            loop: Some((r#None, 0)),",
205                "            sub_entries: [",
206                "                (",
207                "                    entry_type: Chapter,",
208                "                    uid: \"chapter1\",",
209                "                    start_stop: Some((0, 10)),",
210                "                    tags: None,",
211                "                    loop: Some((r#None, 0)),",
212                "                    sub_entries: [",
213                "                        (",
214                "                            entry_type: Chapter,",
215                "                            uid: \"chapter1.1\",",
216                "                            start_stop: Some((0, 4)),",
217                "                            tags: Some((",
218                "                                scope: Stream,",
219                "                                tags: [",
220                "                                    (\"title\", [",
221                "                                        \"chapter 1.1\",",
222                "                                    ]),",
223                "                                ],",
224                "                            )),",
225                "                            loop: Some((r#None, 0)),",
226                "                            sub_entries: [],",
227                "                        ),",
228                "                        (",
229                "                            entry_type: Chapter,",
230                "                            uid: \"chapter1.2\",",
231                "                            start_stop: Some((4, 10)),",
232                "                            tags: Some((",
233                "                                scope: Stream,",
234                "                                tags: [",
235                "                                    (\"title\", [",
236                "                                        \"chapter 1.2\",",
237                "                                    ]),",
238                "                                ],",
239                "                            )),",
240                "                            loop: Some((r#None, 0)),",
241                "                            sub_entries: [],",
242                "                        ),",
243                "                    ],",
244                "                ),",
245                "                (",
246                "                    entry_type: Chapter,",
247                "                    uid: \"chapter2\",",
248                "                    start_stop: Some((10, 15)),",
249                "                    tags: Some((",
250                "                        scope: Stream,",
251                "                        tags: [",
252                "                            (\"title\", [",
253                "                                \"chapter 2\",",
254                "                            ]),",
255                "                        ],",
256                "                    )),",
257                "                    loop: Some((r#None, 0)),",
258                "                    sub_entries: [],",
259                "                ),",
260                "            ],",
261                "        ),",
262                "    ],",
263                ")",
264            )
265            .to_owned()),
266            res,
267        );
268    }
269
270    #[allow(clippy::cognitive_complexity)]
271    #[test]
272    fn test_deserialize() {
273        use crate::tags::Title;
274
275        crate::init().unwrap();
276
277        let toc_ron = r#"
278            (
279                scope: Global,
280                tags: Some((
281                    scope: Stream,
282                    tags: [
283                        ("title", ["toc"]),
284                    ],
285                )),
286                entries: [
287                    (
288                        entry_type: Edition,
289                        uid: "edition",
290                        start_stop: Some((0, 15)),
291                        tags: None,
292                        loop: Some((None, 0)),
293                        sub_entries: [
294                            (
295                                entry_type: Chapter,
296                                uid: "chapter1",
297                                start_stop: Some((0, 10)),
298                                tags: None,
299                                loop: Some((None, 0)),
300                                sub_entries: [
301                                    (
302                                        entry_type: Chapter,
303                                        uid: "chapter1.1",
304                                        start_stop: Some((0, 4)),
305                                        tags: Some((
306                                            scope: Stream,
307                                            tags: [
308                                                ("title", ["chapter 1.1"]),
309                                            ],
310                                        )),
311                                        loop: Some((None, 0)),
312                                        sub_entries: [
313                                        ],
314                                    ),
315                                    (
316                                        entry_type: Chapter,
317                                        uid: "chapter1.2",
318                                        start_stop: Some((4, 10)),
319                                        tags: Some((
320                                            scope: Stream,
321                                            tags: [
322                                                ("title", ["chapter 1.2"]),
323                                            ],
324                                        )),
325                                        loop: Some((None, 0)),
326                                        sub_entries: [
327                                        ],
328                                    ),
329                                ],
330                            ),
331                            (
332                                entry_type: Chapter,
333                                uid: "chapter2",
334                                start_stop: Some((10, 15)),
335                                tags: Some((
336                                    scope: Stream,
337                                    tags: [
338                                        ("title", ["chapter 2"]),
339                                    ],
340                                )),
341                                loop: Some((None, 0)),
342                                sub_entries: [
343                                ],
344                            ),
345                        ],
346                    ),
347                ],
348            )
349        "#;
350        let toc: Toc = ron::de::from_str(toc_ron).unwrap();
351        assert_eq!(toc.scope(), TocScope::Global);
352
353        let entries = toc.entries();
354        assert_eq!(1, entries.len());
355
356        let edition = &entries[0];
357        assert_eq!(TocEntryType::Edition, edition.entry_type());
358        assert_eq!("edition", edition.uid());
359        assert!(edition.tags().is_none());
360        assert_eq!(Some((0, 15)), edition.start_stop_times());
361
362        let sub_entries = edition.sub_entries();
363        assert_eq!(2, sub_entries.len());
364
365        let chapter1 = &sub_entries[0];
366        assert_eq!(TocEntryType::Chapter, chapter1.entry_type());
367        assert_eq!("chapter1", chapter1.uid());
368        assert!(chapter1.tags().is_none());
369        assert_eq!(Some((0, 10)), chapter1.start_stop_times());
370
371        let chap1_sub_entries = chapter1.sub_entries();
372        assert_eq!(2, sub_entries.len());
373
374        let chapter1_1 = &chap1_sub_entries[0];
375        assert_eq!(TocEntryType::Chapter, chapter1_1.entry_type());
376        assert_eq!("chapter1.1", chapter1_1.uid());
377        assert_eq!(Some((0, 4)), chapter1_1.start_stop_times());
378        let tags = chapter1_1.tags().unwrap();
379        assert_eq!("chapter 1.1", tags.index::<Title>(0).unwrap().get());
380        assert_eq!(0, chapter1_1.sub_entries().len());
381
382        let chapter1_2 = &chap1_sub_entries[1];
383        assert_eq!(TocEntryType::Chapter, chapter1_2.entry_type());
384        assert_eq!("chapter1.2", chapter1_2.uid());
385        assert_eq!(Some((4, 10)), chapter1_2.start_stop_times());
386        let tags = chapter1_2.tags().unwrap();
387        assert_eq!("chapter 1.2", tags.index::<Title>(0).unwrap().get());
388        assert_eq!(0, chapter1_2.sub_entries().len());
389
390        let chapter2 = &sub_entries[1];
391        assert_eq!(TocEntryType::Chapter, chapter2.entry_type());
392        assert_eq!("chapter2", chapter2.uid());
393        let tags = chapter2.tags().unwrap();
394        assert_eq!("chapter 2", tags.index::<Title>(0).unwrap().get());
395        assert_eq!(Some((10, 15)), chapter2.start_stop_times());
396        assert_eq!(0, chapter2.sub_entries().len());
397    }
398
399    #[allow(clippy::cognitive_complexity)]
400    #[test]
401    fn test_serde_roundtrip() {
402        crate::init().unwrap();
403
404        let mut toc = Toc::new(TocScope::Global);
405        {
406            let toc = toc.get_mut().unwrap();
407            let mut tags = TagList::new();
408            tags.get_mut()
409                .unwrap()
410                .add::<Title>(&"toc", TagMergeMode::Append);
411            toc.set_tags(tags);
412
413            let mut toc_edition = TocEntry::new(TocEntryType::Edition, "edition");
414            {
415                let toc_edition = toc_edition.get_mut().unwrap();
416                toc_edition.set_start_stop_times(0, 15);
417
418                let mut toc_chap_1 = TocEntry::new(TocEntryType::Chapter, "chapter1");
419                {
420                    let toc_chap_1 = toc_chap_1.get_mut().unwrap();
421                    toc_chap_1.set_start_stop_times(0, 10);
422                    let mut toc_chap_1_1 = TocEntry::new(TocEntryType::Chapter, "chapter1.1");
423                    {
424                        let toc_chap_1_1 = toc_chap_1_1.get_mut().unwrap();
425                        toc_chap_1_1.set_start_stop_times(0, 4);
426                        let mut tags = TagList::new();
427                        tags.get_mut()
428                            .unwrap()
429                            .add::<Title>(&"chapter 1.1", TagMergeMode::Append);
430                        toc_chap_1_1.set_tags(tags);
431                    }
432                    toc_chap_1.append_sub_entry(toc_chap_1_1);
433
434                    let mut toc_chap_1_2 = TocEntry::new(TocEntryType::Chapter, "chapter1.2");
435                    {
436                        let toc_chap_1_2 = toc_chap_1_2.get_mut().unwrap();
437                        toc_chap_1_2.set_start_stop_times(4, 10);
438                        let mut tags = TagList::new();
439                        tags.get_mut()
440                            .unwrap()
441                            .add::<Title>(&"chapter 1.2", TagMergeMode::Append);
442                        toc_chap_1_2.set_tags(tags);
443                    }
444                    toc_chap_1.append_sub_entry(toc_chap_1_2);
445                }
446                toc_edition.append_sub_entry(toc_chap_1);
447
448                let mut toc_chap_2 = TocEntry::new(TocEntryType::Chapter, "chapter2");
449                {
450                    let toc_chap_2 = toc_chap_2.get_mut().unwrap();
451                    toc_chap_2.set_start_stop_times(10, 15);
452                    let mut tags = TagList::new();
453                    tags.get_mut()
454                        .unwrap()
455                        .add::<Title>(&"chapter 2", TagMergeMode::Append);
456                    toc_chap_2.set_tags(tags);
457                }
458                toc_edition.append_sub_entry(toc_chap_2);
459            }
460            toc.append_entry(toc_edition);
461        }
462        let toc_ser = ron::ser::to_string(&toc).unwrap();
463
464        let toc_de: Toc = ron::de::from_str(toc_ser.as_str()).unwrap();
465        assert_eq!(toc_de.scope(), toc.scope());
466
467        let entries_de = toc_de.entries();
468        let entries = toc.entries();
469        assert_eq!(entries_de.len(), entries.len());
470
471        let edition_de = &entries_de[0];
472        let edition = &entries[0];
473        assert_eq!(edition_de.entry_type(), edition.entry_type());
474        assert_eq!(edition_de.uid(), edition.uid());
475        assert_eq!(edition_de.tags(), edition.tags());
476        assert_eq!(edition_de.start_stop_times(), edition.start_stop_times());
477
478        let sub_entries_de = edition_de.sub_entries();
479        let sub_entries = edition.sub_entries();
480        assert_eq!(sub_entries_de.len(), sub_entries.len());
481
482        let chapter1_de = &sub_entries_de[0];
483        let chapter1 = &sub_entries[0];
484        assert_eq!(chapter1_de.entry_type(), chapter1.entry_type());
485        assert_eq!(chapter1_de.uid(), chapter1.uid());
486        assert_eq!(chapter1_de.tags().is_none(), chapter1.tags().is_none());
487        assert_eq!(chapter1_de.start_stop_times(), chapter1.start_stop_times());
488
489        let chap1_sub_entries_de = chapter1_de.sub_entries();
490        let chap1_sub_entries = chapter1.sub_entries();
491        assert_eq!(sub_entries_de.len(), sub_entries.len());
492
493        let chapter1_1_de = &chap1_sub_entries_de[0];
494        let chapter1_1 = &chap1_sub_entries[0];
495        assert_eq!(chapter1_1_de.entry_type(), chapter1_1.entry_type());
496        assert_eq!(chapter1_1_de.uid(), chapter1_1.uid());
497        assert_eq!(
498            chapter1_1_de.start_stop_times(),
499            chapter1_1.start_stop_times()
500        );
501        let tags_de = chapter1_1_de.tags().unwrap();
502        let tags = chapter1_1.tags().unwrap();
503        assert_eq!(
504            tags_de.index::<Title>(0).unwrap().get(),
505            tags.index::<Title>(0).unwrap().get()
506        );
507        assert_eq!(
508            chapter1_1_de.sub_entries().len(),
509            chapter1_1.sub_entries().len()
510        );
511
512        let chapter1_2_de = &chap1_sub_entries_de[1];
513        let chapter1_2 = &chap1_sub_entries[1];
514        assert_eq!(chapter1_2_de.entry_type(), chapter1_2.entry_type());
515        assert_eq!(chapter1_2_de.uid(), chapter1_2.uid());
516        assert_eq!(
517            chapter1_2_de.start_stop_times(),
518            chapter1_2.start_stop_times()
519        );
520        let tags_de = chapter1_2_de.tags().unwrap();
521        let tags = chapter1_2.tags().unwrap();
522        assert_eq!(
523            tags_de.index::<Title>(0).unwrap().get(),
524            tags.index::<Title>(0).unwrap().get()
525        );
526        assert_eq!(
527            chapter1_2_de.sub_entries().len(),
528            chapter1_2.sub_entries().len()
529        );
530
531        let chapter2_de = &sub_entries_de[1];
532        let chapter2 = &sub_entries[1];
533        assert_eq!(chapter2_de.entry_type(), chapter2.entry_type());
534        assert_eq!(chapter2_de.uid(), chapter2.uid());
535        let tags_de = chapter2_de.tags().unwrap();
536        let tags = chapter2.tags().unwrap();
537        assert_eq!(
538            tags_de.index::<Title>(0).unwrap().get(),
539            tags.index::<Title>(0).unwrap().get()
540        );
541        assert_eq!(chapter2_de.start_stop_times(), chapter2.start_stop_times());
542        assert_eq!(
543            chapter2_de.sub_entries().len(),
544            chapter2.sub_entries().len()
545        );
546    }
547}