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