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