1use 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}