1use serde_json::Value as JsonValue;
12use url::Url;
13
14use super::classes::class_dispatch;
15use super::contributor::{Contributor, ContributorEntry, ContributorList, ContributorRole};
16use super::date::EdtfString;
17use super::types::common::{
18 FieldLanguageMap, HasNumbering, LangID, MultilingualString, NumOrStr, NumberingType, Publisher,
19 RefID, RichText, Title,
20};
21use super::types::legal::{Brief, Hearing, LegalCase, Regulation, Statute, Treaty};
22use super::types::specialized::{
23 AudioVisualType, AudioVisualWork, Classic, Dataset, Event, Patent, Software, Standard,
24};
25use super::types::structural::{
26 Collection, CollectionComponent, CollectionType, Monograph, MonographComponentType,
27 MonographType, Serial, SerialComponent, SerialType,
28};
29use super::{
30 ClassExtension, EMPTY_FIELD_LANGUAGES, InputReference, ReferenceClass, UnknownClassData,
31 WorkRelation,
32};
33
34impl InputReference {
35 #[must_use]
37 pub fn class(&self) -> ReferenceClass {
38 self.extension.reference_class()
39 }
40
41 #[must_use]
43 pub fn extension(&self) -> &ClassExtension {
44 &self.extension
45 }
46
47 #[must_use]
49 pub fn extension_mut(&mut self) -> &mut ClassExtension {
50 &mut self.extension
51 }
52
53 #[must_use]
55 pub fn as_monograph(&self) -> Option<&Monograph> {
56 match &self.extension {
57 ClassExtension::Monograph(reference) => Some(reference.as_ref()),
58 _ => None,
59 }
60 }
61
62 #[must_use]
64 pub fn as_collection_component(&self) -> Option<&CollectionComponent> {
65 match &self.extension {
66 ClassExtension::CollectionComponent(reference) => Some(reference.as_ref()),
67 _ => None,
68 }
69 }
70
71 #[must_use]
73 pub fn as_serial_component(&self) -> Option<&SerialComponent> {
74 match &self.extension {
75 ClassExtension::SerialComponent(reference) => Some(reference.as_ref()),
76 _ => None,
77 }
78 }
79
80 #[must_use]
82 pub fn as_collection(&self) -> Option<&Collection> {
83 match &self.extension {
84 ClassExtension::Collection(reference) => Some(reference.as_ref()),
85 _ => None,
86 }
87 }
88
89 #[must_use]
91 pub fn as_serial(&self) -> Option<&Serial> {
92 match &self.extension {
93 ClassExtension::Serial(reference) => Some(reference.as_ref()),
94 _ => None,
95 }
96 }
97
98 #[must_use]
100 pub fn as_legal_case(&self) -> Option<&LegalCase> {
101 match &self.extension {
102 ClassExtension::LegalCase(reference) => Some(reference.as_ref()),
103 _ => None,
104 }
105 }
106
107 #[must_use]
109 pub fn as_statute(&self) -> Option<&Statute> {
110 match &self.extension {
111 ClassExtension::Statute(reference) => Some(reference.as_ref()),
112 _ => None,
113 }
114 }
115
116 #[must_use]
118 pub fn as_treaty(&self) -> Option<&Treaty> {
119 match &self.extension {
120 ClassExtension::Treaty(reference) => Some(reference.as_ref()),
121 _ => None,
122 }
123 }
124
125 #[must_use]
127 pub fn as_hearing(&self) -> Option<&Hearing> {
128 match &self.extension {
129 ClassExtension::Hearing(reference) => Some(reference.as_ref()),
130 _ => None,
131 }
132 }
133
134 #[must_use]
136 pub fn as_regulation(&self) -> Option<&Regulation> {
137 match &self.extension {
138 ClassExtension::Regulation(reference) => Some(reference.as_ref()),
139 _ => None,
140 }
141 }
142
143 #[must_use]
145 pub fn as_brief(&self) -> Option<&Brief> {
146 match &self.extension {
147 ClassExtension::Brief(reference) => Some(reference.as_ref()),
148 _ => None,
149 }
150 }
151
152 #[must_use]
154 pub fn as_classic(&self) -> Option<&Classic> {
155 match &self.extension {
156 ClassExtension::Classic(reference) => Some(reference.as_ref()),
157 _ => None,
158 }
159 }
160
161 #[must_use]
163 pub fn as_patent(&self) -> Option<&Patent> {
164 match &self.extension {
165 ClassExtension::Patent(reference) => Some(reference.as_ref()),
166 _ => None,
167 }
168 }
169
170 #[must_use]
172 pub fn as_dataset(&self) -> Option<&Dataset> {
173 match &self.extension {
174 ClassExtension::Dataset(reference) => Some(reference.as_ref()),
175 _ => None,
176 }
177 }
178
179 #[must_use]
181 pub fn as_standard(&self) -> Option<&Standard> {
182 match &self.extension {
183 ClassExtension::Standard(reference) => Some(reference.as_ref()),
184 _ => None,
185 }
186 }
187
188 #[must_use]
190 pub fn as_software(&self) -> Option<&Software> {
191 match &self.extension {
192 ClassExtension::Software(reference) => Some(reference.as_ref()),
193 _ => None,
194 }
195 }
196
197 #[must_use]
199 pub fn as_event(&self) -> Option<&Event> {
200 match &self.extension {
201 ClassExtension::Event(reference) => Some(reference.as_ref()),
202 _ => None,
203 }
204 }
205
206 #[must_use]
208 pub fn as_audio_visual(&self) -> Option<&AudioVisualWork> {
209 match &self.extension {
210 ClassExtension::AudioVisual(reference) => Some(reference.as_ref()),
211 _ => None,
212 }
213 }
214
215 #[must_use]
217 pub fn unknown_class(&self) -> Option<&UnknownClassData> {
218 match &self.extension {
219 ClassExtension::Unknown(data) => Some(data),
220 _ => None,
221 }
222 }
223
224 fn numbered(&self) -> Option<&dyn HasNumbering> {
225 match &self.extension {
226 ClassExtension::Monograph(reference) => Some(reference.as_ref()),
227 ClassExtension::Collection(reference) => Some(reference.as_ref()),
228 ClassExtension::CollectionComponent(reference) => Some(reference.as_ref()),
229 ClassExtension::SerialComponent(reference) => Some(reference.as_ref()),
230 ClassExtension::Classic(reference) => Some(reference.as_ref()),
231 ClassExtension::AudioVisual(reference) => Some(reference.as_ref()),
232 _ => None,
233 }
234 }
235
236 fn find_numbering(&self, numbering_type: NumberingType) -> Option<String> {
238 self.numbered()
239 .and_then(|reference| reference.find_numbering(numbering_type))
240 }
241
242 pub fn numbering_value(&self, numbering_type: &NumberingType) -> Option<String> {
244 self.find_numbering(numbering_type.clone())
245 }
246
247 pub fn id(&self) -> Option<RefID> {
249 class_dispatch!(&self.extension, |r| r.id.clone(), unknown(data) => data
250 .fields
251 .get("id")
252 .and_then(JsonValue::as_str)
253 .map(|id| RefID(id.to_string())))
254 }
255
256 pub fn author(&self) -> Option<Contributor> {
258 use crate::reference::contributor::ContributorRole as DataRole;
259
260 match &self.extension {
261 ClassExtension::Monograph(r) => {
262 collect_contributors_by_role(&r.contributors, &DataRole::Author)
263 .or_else(|| r.author.clone())
264 }
265 ClassExtension::CollectionComponent(r) => {
266 collect_contributors_by_role(&r.contributors, &DataRole::Author)
267 .or_else(|| r.author.clone())
268 }
269 ClassExtension::SerialComponent(r) => {
270 let explicit_author =
271 collect_contributors_by_role(&r.contributors, &DataRole::Author)
272 .or_else(|| r.author.clone());
273
274 explicit_author.or_else(|| {
275 let av_like = r
276 .medium
277 .as_deref()
278 .map(|value| {
279 let lowered = value.to_ascii_lowercase();
280 lowered.contains("podcast")
281 || lowered.contains("tv")
282 || lowered.contains("film")
283 || lowered.contains("video")
284 })
285 .unwrap_or(false)
286 || r.genre
287 .as_deref()
288 .map(|value| {
289 let lowered = value.to_ascii_lowercase();
290 lowered.contains("broadcast") || lowered.contains("film")
291 })
292 .unwrap_or(false);
293
294 if av_like {
295 collect_contributors_by_role(&r.contributors, &DataRole::Producer).or_else(
296 || collect_contributors_by_role(&r.contributors, &DataRole::Host),
297 )
298 } else {
299 None
300 }
301 })
302 }
303 ClassExtension::Treaty(r) => r.author.clone(),
304 ClassExtension::Brief(r) => r.author.clone(),
305 ClassExtension::Classic(r) => r.author.clone(),
306 ClassExtension::Patent(r) => r.author.clone(),
307 ClassExtension::Dataset(r) => r.author.clone(),
308 ClassExtension::Software(r) => r.author.clone(),
309 ClassExtension::Event(r) => {
310 collect_contributors_by_role(&r.contributors, &DataRole::Performer)
311 .or_else(|| {
312 collect_contributors_by_role(
313 &r.contributors,
314 &DataRole::Unknown("organizer".to_string()),
315 )
316 })
317 .or_else(|| collect_contributors_by_role(&r.contributors, &DataRole::Author))
318 }
319 ClassExtension::AudioVisual(r) => {
320 let explicit_author =
321 collect_contributors_by_role(&r.core.contributors, &DataRole::Author);
322
323 match r.r#type {
324 AudioVisualType::Film | AudioVisualType::Episode => {
325 explicit_author.or_else(|| {
326 collect_contributors_by_role(&r.core.contributors, &DataRole::Director)
327 })
328 }
329 AudioVisualType::Recording => explicit_author.or_else(|| {
330 collect_contributors_by_role(&r.core.contributors, &DataRole::Composer)
331 .or_else(|| {
332 collect_contributors_by_role(
333 &r.core.contributors,
334 &DataRole::Performer,
335 )
336 })
337 }),
338 AudioVisualType::Broadcast => explicit_author
339 .or_else(|| {
340 collect_contributors_by_role(&r.core.contributors, &DataRole::Director)
341 })
342 .or_else(|| {
343 collect_contributors_by_role(&r.core.contributors, &DataRole::Producer)
344 }),
345 }
346 }
347 _ => None,
348 }
349 }
350
351 pub fn editor(&self) -> Option<Contributor> {
352 match &self.extension {
353 ClassExtension::Monograph(r) => {
354 collect_contributors_by_role(&r.contributors, &ContributorRole::Editor)
355 .or_else(|| r.editor.clone())
356 }
357 ClassExtension::Collection(r) => {
358 collect_contributors_by_role(&r.contributors, &ContributorRole::Editor)
359 .or_else(|| r.editor.clone())
360 }
361 ClassExtension::CollectionComponent(r) => r
362 .container
363 .as_ref()
364 .and_then(|c| match c {
365 WorkRelation::Embedded(p) => p.editor(),
366 WorkRelation::Id(_) => None,
367 })
368 .or_else(|| {
369 collect_contributors_by_role(&r.contributors, &ContributorRole::Editor)
370 }),
371 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
372 WorkRelation::Embedded(p) => p.editor(),
373 WorkRelation::Id(_) => None,
374 }),
375 ClassExtension::Serial(r) => {
376 collect_contributors_by_role(&r.contributors, &ContributorRole::Editor)
377 .or_else(|| r.editor.clone())
378 }
379 ClassExtension::Classic(r) => r.editor.clone(),
380 ClassExtension::AudioVisual(_) => None,
381 _ => None,
382 }
383 }
384
385 pub fn translator(&self) -> Option<Contributor> {
387 match &self.extension {
388 ClassExtension::Monograph(r) => {
389 collect_contributors_by_role(&r.contributors, &ContributorRole::Translator)
390 .or_else(|| r.translator.clone())
391 }
392 ClassExtension::CollectionComponent(r) => {
393 collect_contributors_by_role(&r.contributors, &ContributorRole::Translator)
394 .or_else(|| r.translator.clone())
395 }
396 ClassExtension::SerialComponent(r) => {
397 collect_contributors_by_role(&r.contributors, &ContributorRole::Translator)
398 .or_else(|| r.translator.clone())
399 }
400 ClassExtension::Collection(r) => {
401 collect_contributors_by_role(&r.contributors, &ContributorRole::Translator)
402 .or_else(|| r.translator.clone())
403 }
404 ClassExtension::Classic(r) => r.translator.clone(),
405 ClassExtension::AudioVisual(r) => {
406 collect_contributors_by_role(&r.core.contributors, &ContributorRole::Translator)
407 }
408 _ => None,
409 }
410 }
411
412 pub fn publisher(&self) -> Option<Publisher> {
414 match &self.extension {
415 ClassExtension::Monograph(r) => r.publisher.clone(),
416 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
417 WorkRelation::Embedded(p) => p.publisher(),
418 WorkRelation::Id(_) => None,
419 }),
420 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
421 WorkRelation::Embedded(p) => p.publisher(),
422 WorkRelation::Id(_) => None,
423 }),
424 ClassExtension::Collection(r) => r.publisher.clone(),
425 ClassExtension::Serial(r) => r.publisher.clone(),
426 ClassExtension::Classic(r) => r.publisher.clone(),
427 ClassExtension::Dataset(r) => r.publisher.clone(),
428 ClassExtension::Standard(r) => r.publisher.clone(),
429 ClassExtension::Software(r) => r.publisher.clone(),
430 ClassExtension::AudioVisual(r) => r.publisher.clone(),
431 _ => None,
432 }
433 }
434
435 pub fn contributor(&self, role: ContributorRole) -> Option<Contributor> {
441 let entries = self.all_contributor_entries();
442 collect_contributors_by_role(entries, &role)
443 }
444
445 pub fn contributor_entries(&self, role: &ContributorRole) -> Vec<&ContributorEntry> {
447 self.all_contributor_entries()
448 .iter()
449 .filter(|entry| &entry.role == role)
450 .collect()
451 }
452
453 pub fn all_contributor_entries(&self) -> &[ContributorEntry] {
455 match &self.extension {
456 ClassExtension::Monograph(r) => &r.contributors,
457 ClassExtension::Collection(r) => &r.contributors,
458 ClassExtension::CollectionComponent(r) => &r.contributors,
459 ClassExtension::Serial(r) => &r.contributors,
460 ClassExtension::SerialComponent(r) => &r.contributors,
461 ClassExtension::Event(r) => &r.contributors,
462 ClassExtension::AudioVisual(r) => &r.core.contributors,
463 _ => &[],
464 }
465 }
466
467 pub fn title(&self) -> Option<Title> {
469 match &self.extension {
470 ClassExtension::Monograph(r) => match (&r.title, &r.short_title) {
471 (Some(Title::Single(long)), Some(short)) => {
472 Some(Title::Shorthand(short.clone(), long.clone()))
473 }
474 _ => r.title.clone(),
475 },
476 ClassExtension::CollectionComponent(r) => r.title.clone(),
477 ClassExtension::SerialComponent(r) => r.title.clone(),
478 ClassExtension::Collection(r) => match (&r.title, &r.short_title) {
479 (Some(Title::Single(long)), Some(short)) => {
480 Some(Title::Shorthand(short.clone(), long.clone()))
481 }
482 _ => r.title.clone(),
483 },
484 ClassExtension::Serial(r) => match (&r.title, &r.short_title) {
485 (Some(Title::Single(long)), Some(short)) => {
486 Some(Title::Shorthand(short.clone(), long.clone()))
487 }
488 _ => r.title.clone(),
489 },
490 ClassExtension::LegalCase(r) => r.title.clone(),
491 ClassExtension::Statute(r) => r.title.clone(),
492 ClassExtension::Treaty(r) => r.title.clone(),
493 ClassExtension::Hearing(r) => r.title.clone(),
494 ClassExtension::Regulation(r) => r.title.clone(),
495 ClassExtension::Brief(r) => r.title.clone(),
496 ClassExtension::Classic(r) => r.title.clone(),
497 ClassExtension::Patent(r) => r.title.clone(),
498 ClassExtension::Dataset(r) => r.title.clone(),
499 ClassExtension::Standard(r) => r.title.clone(),
500 ClassExtension::Software(r) => r.title.clone(),
501 ClassExtension::Event(r) => r.title.clone(),
502 ClassExtension::AudioVisual(r) => match (&r.core.title, &r.core.short_title) {
503 (Some(Title::Single(long)), Some(short)) => {
504 Some(Title::Shorthand(short.clone(), long.clone()))
505 }
506 _ => r.core.title.clone(),
507 },
508 ClassExtension::Unknown(data) => data
509 .fields
510 .get("title")
511 .and_then(JsonValue::as_str)
512 .map(|title| Title::Single(title.to_string())),
513 }
514 }
515
516 fn non_empty_date(date: EdtfString) -> Option<EdtfString> {
517 if date.is_empty() { None } else { Some(date) }
518 }
519
520 pub fn created(&self) -> Option<EdtfString> {
522 match &self.extension {
523 ClassExtension::Monograph(r) => Self::non_empty_date(r.created.clone()),
524 ClassExtension::CollectionComponent(r) => Self::non_empty_date(r.created.clone()),
525 ClassExtension::SerialComponent(r) => Self::non_empty_date(r.created.clone()),
526 ClassExtension::Collection(r) => Self::non_empty_date(r.created.clone()),
527 ClassExtension::Serial(_) => None,
528 ClassExtension::LegalCase(r) => Self::non_empty_date(r.created.clone()),
529 ClassExtension::Statute(r) => Self::non_empty_date(r.created.clone()),
530 ClassExtension::Treaty(r) => Self::non_empty_date(r.created.clone()),
531 ClassExtension::Hearing(r) => Self::non_empty_date(r.created.clone()),
532 ClassExtension::Regulation(r) => Self::non_empty_date(r.created.clone()),
533 ClassExtension::Brief(r) => Self::non_empty_date(r.created.clone()),
534 ClassExtension::Classic(r) => Self::non_empty_date(r.created.clone()),
535 ClassExtension::Patent(r) => Self::non_empty_date(r.created.clone()),
536 ClassExtension::Dataset(r) => Self::non_empty_date(r.created.clone()),
537 ClassExtension::Standard(r) => Self::non_empty_date(r.created.clone()),
538 ClassExtension::Software(r) => Self::non_empty_date(r.created.clone()),
539 ClassExtension::Event(_) => None,
540 ClassExtension::AudioVisual(r) => Self::non_empty_date(r.core.created.clone()),
541 ClassExtension::Unknown(_) => None,
542 }
543 }
544
545 pub fn issued(&self) -> Option<EdtfString> {
547 match &self.extension {
548 ClassExtension::Monograph(r) => Self::non_empty_date(r.issued.clone()),
549 ClassExtension::CollectionComponent(r) => Self::non_empty_date(r.issued.clone()),
550 ClassExtension::SerialComponent(r) => Self::non_empty_date(r.issued.clone()),
551 ClassExtension::Collection(r) => Self::non_empty_date(r.issued.clone()),
552 ClassExtension::Serial(_) => None,
553 ClassExtension::LegalCase(r) => Self::non_empty_date(r.issued.clone()),
554 ClassExtension::Statute(r) => Self::non_empty_date(r.issued.clone()),
555 ClassExtension::Treaty(r) => Self::non_empty_date(r.issued.clone()),
556 ClassExtension::Hearing(r) => Self::non_empty_date(r.issued.clone()),
557 ClassExtension::Regulation(r) => Self::non_empty_date(r.issued.clone()),
558 ClassExtension::Brief(r) => Self::non_empty_date(r.issued.clone()),
559 ClassExtension::Classic(r) => Self::non_empty_date(r.issued.clone()),
560 ClassExtension::Patent(r) => Self::non_empty_date(r.issued.clone()),
561 ClassExtension::Dataset(r) => Self::non_empty_date(r.issued.clone()),
562 ClassExtension::Standard(r) => Self::non_empty_date(r.issued.clone()),
563 ClassExtension::Software(r) => Self::non_empty_date(r.issued.clone()),
564 ClassExtension::Event(r) => r.date.clone(),
565 ClassExtension::AudioVisual(r) => Self::non_empty_date(r.core.issued.clone()),
566 ClassExtension::Unknown(_) => None,
567 }
568 }
569
570 pub fn effective_issued_date(&self) -> Option<EdtfString> {
573 self.issued().or_else(|| self.created())
574 }
575
576 pub fn doi(&self) -> Option<String> {
578 match &self.extension {
579 ClassExtension::Monograph(r) => r.doi.clone(),
580 ClassExtension::CollectionComponent(r) => r.doi.clone(),
581 ClassExtension::SerialComponent(r) => r.doi.clone(),
582 ClassExtension::LegalCase(r) => r.doi.clone(),
583 ClassExtension::Dataset(r) => r.doi.clone(),
584 ClassExtension::Standard(r) => r.doi.clone(),
585 ClassExtension::Software(r) => r.doi.clone(),
586 ClassExtension::AudioVisual(_) => None,
587 _ => None,
588 }
589 }
590
591 pub fn ads_bibcode(&self) -> Option<String> {
593 match &self.extension {
594 ClassExtension::Monograph(r) => r.ads_bibcode.clone(),
595 ClassExtension::SerialComponent(r) => r.ads_bibcode.clone(),
596 ClassExtension::AudioVisual(_) => None,
597 _ => None,
598 }
599 }
600
601 pub fn note(&self) -> Option<RichText> {
603 match &self.extension {
604 ClassExtension::Monograph(r) => r.note.clone(),
605 ClassExtension::CollectionComponent(r) => r.note.clone(),
606 ClassExtension::SerialComponent(r) => r.note.clone(),
607 ClassExtension::Collection(r) => r.note.clone(),
608 ClassExtension::Serial(r) => r.note.clone(),
609 ClassExtension::LegalCase(r) => r.note.clone(),
610 ClassExtension::Statute(r) => r.note.clone(),
611 ClassExtension::Treaty(r) => r.note.clone(),
612 ClassExtension::Standard(r) => r.note.clone(),
613 ClassExtension::Event(r) => r.note.clone(),
614 ClassExtension::AudioVisual(r) => r.note.clone(),
615 _ => None,
616 }
617 }
618
619 pub fn url(&self) -> Option<Url> {
621 match &self.extension {
622 ClassExtension::Monograph(r) => r.url.clone(),
623 ClassExtension::CollectionComponent(r) => r.url.clone(),
624 ClassExtension::SerialComponent(r) => r.url.clone(),
625 ClassExtension::Collection(r) => r.url.clone(),
626 ClassExtension::Serial(r) => r.url.clone(),
627 ClassExtension::LegalCase(r) => r.url.clone(),
628 ClassExtension::Statute(r) => r.url.clone(),
629 ClassExtension::Treaty(r) => r.url.clone(),
630 ClassExtension::Hearing(r) => r.url.clone(),
631 ClassExtension::Regulation(r) => r.url.clone(),
632 ClassExtension::Brief(r) => r.url.clone(),
633 ClassExtension::Classic(r) => r.url.clone(),
634 ClassExtension::Patent(r) => r.url.clone(),
635 ClassExtension::Dataset(r) => r.url.clone(),
636 ClassExtension::Standard(r) => r.url.clone(),
637 ClassExtension::Software(r) => r.url.clone(),
638 ClassExtension::Event(r) => r.url.clone(),
639 ClassExtension::AudioVisual(r) => r.url.clone(),
640 ClassExtension::Unknown(_) => None,
641 }
642 }
643
644 pub fn publisher_place(&self) -> Option<String> {
646 match &self.extension {
647 ClassExtension::Monograph(r) => r
648 .publisher
649 .as_ref()
650 .and_then(|p| p.place.clone().map(Into::into))
651 .or_else(|| {
652 r.container.as_ref().and_then(|c| match c {
653 WorkRelation::Embedded(p) => p.publisher_place(),
654 _ => None,
655 })
656 }),
657 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
658 WorkRelation::Embedded(p) => p.publisher_place(),
659 _ => None,
660 }),
661 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
662 WorkRelation::Embedded(p) => p.publisher_place(),
663 _ => None,
664 }),
665 ClassExtension::Collection(r) => r
666 .publisher
667 .as_ref()
668 .and_then(|p| p.place.clone().map(Into::into))
669 .or_else(|| {
670 r.container.as_ref().and_then(|c| match c {
671 WorkRelation::Embedded(p) => p.publisher_place(),
672 _ => None,
673 })
674 }),
675 ClassExtension::Serial(_) => None,
676 ClassExtension::Classic(r) => r
677 .publisher
678 .as_ref()
679 .and_then(|p| p.place.clone().map(Into::into)),
680 ClassExtension::Dataset(r) => r
681 .publisher
682 .as_ref()
683 .and_then(|p| p.place.clone().map(Into::into)),
684 ClassExtension::Standard(r) => r
685 .publisher
686 .as_ref()
687 .and_then(|p| p.place.clone().map(Into::into)),
688 ClassExtension::Software(r) => r
689 .publisher
690 .as_ref()
691 .and_then(|p| p.place.clone().map(Into::into)),
692 ClassExtension::Event(r) => r.location.clone(),
693 ClassExtension::AudioVisual(r) => r
694 .publisher
695 .as_ref()
696 .and_then(|p| p.place.clone().map(Into::into)),
697 _ => None,
698 }
699 }
700
701 pub fn publisher_str(&self) -> Option<String> {
703 match &self.extension {
704 ClassExtension::Monograph(r) => r
705 .publisher
706 .as_ref()
707 .map(|p| p.name.to_string())
708 .or_else(|| {
709 r.container.as_ref().and_then(|c| match c {
710 WorkRelation::Embedded(p) => p.publisher_str(),
711 _ => None,
712 })
713 }),
714 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
715 WorkRelation::Embedded(p) => p.publisher_str(),
716 _ => None,
717 }),
718 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
719 WorkRelation::Embedded(p) => p.publisher_str(),
720 _ => None,
721 }),
722 ClassExtension::Collection(r) => r
723 .publisher
724 .as_ref()
725 .map(|p| p.name.to_string())
726 .or_else(|| {
727 r.container.as_ref().and_then(|c| match c {
728 WorkRelation::Embedded(p) => p.publisher_str(),
729 _ => None,
730 })
731 }),
732 ClassExtension::Serial(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
733 ClassExtension::Classic(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
734 ClassExtension::Dataset(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
735 ClassExtension::Standard(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
736 ClassExtension::Software(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
737 ClassExtension::Event(r) => r.network.clone(),
738 ClassExtension::AudioVisual(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
739 _ => None,
740 }
741 }
742
743 pub(super) fn normalize_genre_medium(s: &str) -> String {
747 let lower = s.to_ascii_lowercase();
748 lower
749 .split(|c: char| c.is_whitespace() || c == '_')
750 .filter(|p| !p.is_empty())
751 .collect::<Vec<_>>()
752 .join("-")
753 }
754
755 pub fn genre(&self) -> Option<String> {
757 match &self.extension {
758 ClassExtension::Monograph(r) => {
759 r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
760 }
761 ClassExtension::CollectionComponent(r) => {
762 r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
763 }
764 ClassExtension::SerialComponent(r) => {
765 r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
766 }
767 ClassExtension::Event(r) => r.genre.as_ref().map(|g| Self::normalize_genre_medium(g)),
768 ClassExtension::AudioVisual(r) => r
769 .core
770 .genre
771 .as_ref()
772 .map(|g| Self::normalize_genre_medium(g)),
773 ClassExtension::Dataset(r) => r.genre.as_ref().map(|g| Self::normalize_genre_medium(g)),
774 _ => None,
775 }
776 }
777
778 pub fn archive(&self) -> Option<String> {
780 match &self.extension {
781 ClassExtension::Monograph(r) => r.archive.clone(),
782 _ => None,
783 }
784 }
785
786 pub fn archive_location(&self) -> Option<String> {
788 match &self.extension {
789 ClassExtension::Monograph(r) => r
790 .archive_info
791 .as_ref()
792 .and_then(|info| info.location.clone())
793 .or_else(|| r.archive_location.clone()),
794 ClassExtension::CollectionComponent(r) => {
795 r.archive_info.as_ref().and_then(|i| i.location.clone())
796 }
797 ClassExtension::SerialComponent(r) => {
798 r.archive_info.as_ref().and_then(|i| i.location.clone())
799 }
800 _ => None,
801 }
802 }
803
804 pub fn archive_name(&self) -> Option<MultilingualString> {
806 match &self.extension {
807 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.name.clone()),
808 ClassExtension::CollectionComponent(r) => {
809 r.archive_info.as_ref().and_then(|i| i.name.clone())
810 }
811 ClassExtension::SerialComponent(r) => {
812 r.archive_info.as_ref().and_then(|i| i.name.clone())
813 }
814 _ => None,
815 }
816 }
817
818 pub fn archive_place(&self) -> Option<String> {
820 match &self.extension {
821 ClassExtension::Monograph(r) => r
822 .archive_info
823 .as_ref()
824 .and_then(|i| i.place.clone().map(Into::into)),
825 ClassExtension::CollectionComponent(r) => r
826 .archive_info
827 .as_ref()
828 .and_then(|i| i.place.clone().map(Into::into)),
829 ClassExtension::SerialComponent(r) => r
830 .archive_info
831 .as_ref()
832 .and_then(|i| i.place.clone().map(Into::into)),
833 _ => None,
834 }
835 }
836
837 pub fn archive_collection(&self) -> Option<String> {
839 match &self.extension {
840 ClassExtension::Monograph(r) => {
841 r.archive_info.as_ref().and_then(|i| i.collection.clone())
842 }
843 ClassExtension::CollectionComponent(r) => {
844 r.archive_info.as_ref().and_then(|i| i.collection.clone())
845 }
846 ClassExtension::SerialComponent(r) => {
847 r.archive_info.as_ref().and_then(|i| i.collection.clone())
848 }
849 _ => None,
850 }
851 }
852
853 pub fn archive_collection_id(&self) -> Option<String> {
855 match &self.extension {
856 ClassExtension::Monograph(r) => r
857 .archive_info
858 .as_ref()
859 .and_then(|i| i.collection_id.clone()),
860 ClassExtension::CollectionComponent(r) => r
861 .archive_info
862 .as_ref()
863 .and_then(|i| i.collection_id.clone()),
864 ClassExtension::SerialComponent(r) => r
865 .archive_info
866 .as_ref()
867 .and_then(|i| i.collection_id.clone()),
868 _ => None,
869 }
870 }
871
872 pub fn archive_series(&self) -> Option<String> {
874 match &self.extension {
875 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.series.clone()),
876 ClassExtension::CollectionComponent(r) => {
877 r.archive_info.as_ref().and_then(|i| i.series.clone())
878 }
879 ClassExtension::SerialComponent(r) => {
880 r.archive_info.as_ref().and_then(|i| i.series.clone())
881 }
882 _ => None,
883 }
884 }
885
886 pub fn archive_box(&self) -> Option<String> {
888 match &self.extension {
889 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.r#box.clone()),
890 ClassExtension::CollectionComponent(r) => {
891 r.archive_info.as_ref().and_then(|i| i.r#box.clone())
892 }
893 ClassExtension::SerialComponent(r) => {
894 r.archive_info.as_ref().and_then(|i| i.r#box.clone())
895 }
896 _ => None,
897 }
898 }
899
900 pub fn archive_folder(&self) -> Option<String> {
902 match &self.extension {
903 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.folder.clone()),
904 ClassExtension::CollectionComponent(r) => {
905 r.archive_info.as_ref().and_then(|i| i.folder.clone())
906 }
907 ClassExtension::SerialComponent(r) => {
908 r.archive_info.as_ref().and_then(|i| i.folder.clone())
909 }
910 _ => None,
911 }
912 }
913
914 pub fn archive_item(&self) -> Option<String> {
916 match &self.extension {
917 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.item.clone()),
918 ClassExtension::CollectionComponent(r) => {
919 r.archive_info.as_ref().and_then(|i| i.item.clone())
920 }
921 ClassExtension::SerialComponent(r) => {
922 r.archive_info.as_ref().and_then(|i| i.item.clone())
923 }
924 _ => None,
925 }
926 }
927
928 pub fn archive_url(&self) -> Option<Url> {
930 match &self.extension {
931 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.url.clone()),
932 ClassExtension::CollectionComponent(r) => {
933 r.archive_info.as_ref().and_then(|i| i.url.clone())
934 }
935 ClassExtension::SerialComponent(r) => {
936 r.archive_info.as_ref().and_then(|i| i.url.clone())
937 }
938 _ => None,
939 }
940 }
941
942 pub fn status(&self) -> Option<String> {
944 match &self.extension {
945 ClassExtension::Monograph(r) => r.status.clone(),
946 ClassExtension::CollectionComponent(r) => r.status.clone(),
947 ClassExtension::SerialComponent(r) => r.status.clone(),
948 ClassExtension::Standard(r) => r.status.clone(),
949 _ => None,
950 }
951 }
952
953 pub fn eprint_id(&self) -> Option<String> {
955 match &self.extension {
956 ClassExtension::Monograph(r) => r.eprint.as_ref().map(|e| e.id.clone()),
957 ClassExtension::CollectionComponent(r) => r.eprint.as_ref().map(|e| e.id.clone()),
958 ClassExtension::SerialComponent(r) => r.eprint.as_ref().map(|e| e.id.clone()),
959 _ => None,
960 }
961 }
962
963 pub fn eprint_server(&self) -> Option<String> {
965 match &self.extension {
966 ClassExtension::Monograph(r) => r.eprint.as_ref().map(|e| e.server.clone()),
967 ClassExtension::CollectionComponent(r) => r.eprint.as_ref().map(|e| e.server.clone()),
968 ClassExtension::SerialComponent(r) => r.eprint.as_ref().map(|e| e.server.clone()),
969 _ => None,
970 }
971 }
972
973 pub fn eprint_class(&self) -> Option<String> {
975 match &self.extension {
976 ClassExtension::Monograph(r) => r.eprint.as_ref().and_then(|e| e.class.clone()),
977 ClassExtension::CollectionComponent(r) => {
978 r.eprint.as_ref().and_then(|e| e.class.clone())
979 }
980 ClassExtension::SerialComponent(r) => r.eprint.as_ref().and_then(|e| e.class.clone()),
981 _ => None,
982 }
983 }
984
985 pub fn medium(&self) -> Option<String> {
987 match &self.extension {
988 ClassExtension::Monograph(r) => {
989 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
990 }
991 ClassExtension::CollectionComponent(r) => {
992 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
993 }
994 ClassExtension::SerialComponent(r) => {
995 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
996 }
997 ClassExtension::AudioVisual(r) => {
998 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
999 }
1000 _ => None,
1001 }
1002 }
1003
1004 pub fn version(&self) -> Option<String> {
1006 match &self.extension {
1007 ClassExtension::Dataset(r) => r.version.clone(),
1008 ClassExtension::Software(r) => r.version.clone(),
1009 _ => None,
1010 }
1011 }
1012
1013 pub fn abstract_text(&self) -> Option<RichText> {
1015 match &self.extension {
1016 ClassExtension::Monograph(r) => r.abstract_text.clone(),
1017 ClassExtension::CollectionComponent(r) => r.abstract_text.clone(),
1018 ClassExtension::SerialComponent(r) => r.abstract_text.clone(),
1019 _ => None,
1020 }
1021 }
1022
1023 pub fn container_title(&self) -> Option<Title> {
1025 match &self.extension {
1026 ClassExtension::Monograph(r) => r.container.as_ref().and_then(|c| match c {
1027 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1028 WorkRelation::Id(_) => None,
1029 }),
1030 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
1031 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1032 WorkRelation::Id(_) => None,
1033 }),
1034 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
1035 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1036 WorkRelation::Id(_) => None,
1037 }),
1038 ClassExtension::Serial(r) => r.container.as_ref().and_then(|c| match c {
1039 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1040 WorkRelation::Id(_) => None,
1041 }),
1042 ClassExtension::LegalCase(r) => r.reporter.clone().map(Title::Single),
1043 ClassExtension::Statute(r) => r.code.clone().map(Title::Single),
1044 ClassExtension::Treaty(r) => r.reporter.clone().map(Title::Single),
1045 ClassExtension::Event(r) => r.container.as_ref().and_then(|c| match c {
1046 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1047 WorkRelation::Id(_) => None,
1048 }),
1049 ClassExtension::AudioVisual(r) => r.container.as_ref().and_then(|c| match c {
1050 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1051 WorkRelation::Id(_) => None,
1052 }),
1053 _ => None,
1054 }
1055 }
1056
1057 pub fn collection_title(&self) -> Option<Title> {
1059 fn nested_collection_title(relation: &WorkRelation) -> Option<Title> {
1060 let WorkRelation::Embedded(parent) = relation else {
1061 return None;
1062 };
1063 let container = match parent.extension() {
1064 ClassExtension::Monograph(r) => r.container.as_ref(),
1065 ClassExtension::Collection(r) => r.container.as_ref(),
1066 ClassExtension::Serial(r) => r.container.as_ref(),
1067 _ => None,
1068 }?;
1069 match container {
1070 WorkRelation::Embedded(collection) => collection.title(),
1071 WorkRelation::Id(_) => None,
1072 }
1073 }
1074
1075 match &self.extension {
1076 ClassExtension::Monograph(r) => r.container.as_ref().and_then(nested_collection_title),
1077 ClassExtension::CollectionComponent(r) => {
1078 r.container.as_ref().and_then(nested_collection_title)
1079 }
1080 ClassExtension::SerialComponent(r) => {
1081 r.container.as_ref().and_then(nested_collection_title)
1082 }
1083 _ => None,
1084 }
1085 }
1086
1087 pub fn volume(&self) -> Option<NumOrStr> {
1089 match &self.extension {
1090 ClassExtension::Monograph(r) => r
1091 .volume
1092 .clone()
1093 .or_else(|| self.find_numbering(NumberingType::Volume))
1094 .map(NumOrStr::Str),
1095 ClassExtension::Collection(r) => r
1096 .volume
1097 .clone()
1098 .or_else(|| self.find_numbering(NumberingType::Volume))
1099 .map(NumOrStr::Str),
1100 ClassExtension::CollectionComponent(r) => r
1101 .volume
1102 .clone()
1103 .or_else(|| self.find_numbering(NumberingType::Volume))
1104 .map(NumOrStr::Str),
1105 ClassExtension::SerialComponent(r) => r
1106 .volume
1107 .clone()
1108 .or_else(|| self.find_numbering(NumberingType::Volume))
1109 .map(NumOrStr::Str),
1110 ClassExtension::Classic(r) => r
1111 .volume
1112 .clone()
1113 .or_else(|| self.find_numbering(NumberingType::Volume))
1114 .map(NumOrStr::Str),
1115 ClassExtension::LegalCase(r) => r.volume.clone().map(NumOrStr::Str),
1116 ClassExtension::Statute(r) => r.volume.clone().map(NumOrStr::Str),
1117 ClassExtension::Treaty(r) => r.volume.clone().map(NumOrStr::Str),
1118 ClassExtension::Regulation(r) => r.volume.clone().map(NumOrStr::Str),
1119 _ => self
1120 .find_numbering(NumberingType::Volume)
1121 .map(NumOrStr::Str),
1122 }
1123 }
1124
1125 pub fn collection_number(&self) -> Option<String> {
1127 match &self.extension {
1128 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
1129 WorkRelation::Embedded(p) => p.collection_number(),
1130 WorkRelation::Id(_) => None,
1131 }),
1132 _ => self.find_numbering(NumberingType::Volume),
1133 }
1134 }
1135
1136 pub fn issue(&self) -> Option<NumOrStr> {
1138 match &self.extension {
1139 ClassExtension::Monograph(r) => r
1140 .issue
1141 .clone()
1142 .or_else(|| self.find_numbering(NumberingType::Issue))
1143 .map(NumOrStr::Str),
1144 ClassExtension::Collection(r) => r
1145 .issue
1146 .clone()
1147 .or_else(|| self.find_numbering(NumberingType::Issue))
1148 .map(NumOrStr::Str),
1149 ClassExtension::CollectionComponent(r) => r
1150 .issue
1151 .clone()
1152 .or_else(|| self.find_numbering(NumberingType::Issue))
1153 .map(NumOrStr::Str),
1154 ClassExtension::SerialComponent(r) => r
1155 .issue
1156 .clone()
1157 .or_else(|| self.find_numbering(NumberingType::Issue))
1158 .map(NumOrStr::Str),
1159 ClassExtension::Classic(r) => r
1160 .issue
1161 .clone()
1162 .or_else(|| self.find_numbering(NumberingType::Issue))
1163 .map(NumOrStr::Str),
1164 _ => self.find_numbering(NumberingType::Issue).map(NumOrStr::Str),
1165 }
1166 }
1167
1168 pub fn pages(&self) -> Option<NumOrStr> {
1170 match &self.extension {
1171 ClassExtension::CollectionComponent(r) => r.pages.clone(),
1172 ClassExtension::SerialComponent(r) => r.pages.clone().map(NumOrStr::Str),
1173 ClassExtension::LegalCase(r) => r.page.clone().map(NumOrStr::Str),
1174 ClassExtension::Statute(r) => r.page.clone().map(NumOrStr::Str),
1175 ClassExtension::Treaty(r) => r.page.clone().map(NumOrStr::Str),
1176 _ => None,
1177 }
1178 }
1179
1180 pub fn authority(&self) -> Option<String> {
1182 match &self.extension {
1183 ClassExtension::LegalCase(r) => r.authority.clone(),
1184 ClassExtension::Statute(r) => r.authority.clone(),
1185 ClassExtension::Hearing(r) => r.authority.clone(),
1186 ClassExtension::Regulation(r) => r.authority.clone(),
1187 ClassExtension::Brief(r) => r.authority.clone(),
1188 ClassExtension::Patent(r) => r.authority.clone(),
1189 ClassExtension::Standard(r) => r.authority.clone(),
1190 _ => None,
1191 }
1192 }
1193
1194 pub fn reporter(&self) -> Option<String> {
1196 match &self.extension {
1197 ClassExtension::LegalCase(r) => r.reporter.clone(),
1198 ClassExtension::Treaty(r) => r.reporter.clone(),
1199 _ => None,
1200 }
1201 }
1202
1203 pub fn code(&self) -> Option<String> {
1205 match &self.extension {
1206 ClassExtension::Statute(r) => r.code.clone(),
1207 ClassExtension::Regulation(r) => r.code.clone(),
1208 _ => None,
1209 }
1210 }
1211
1212 pub fn section(&self) -> Option<String> {
1214 match &self.extension {
1215 ClassExtension::Statute(r) => r.section.clone(),
1216 ClassExtension::Regulation(r) => r.section.clone(),
1217 ClassExtension::Classic(_) => self.find_numbering(NumberingType::Section),
1218 ClassExtension::SerialComponent(r) => r.section.clone(),
1219 _ => None,
1220 }
1221 }
1222
1223 pub fn number(&self) -> Option<String> {
1225 match &self.extension {
1226 ClassExtension::Monograph(r) => r
1227 .number
1228 .clone()
1229 .or_else(|| self.find_numbering(NumberingType::Number)),
1230 ClassExtension::Statute(r) => r.number.clone(),
1231 ClassExtension::Hearing(r) => r.session_number.clone(),
1232 ClassExtension::Collection(r) => r
1233 .number
1234 .clone()
1235 .or_else(|| self.find_numbering(NumberingType::Number)),
1236 ClassExtension::CollectionComponent(r) => r
1237 .number
1238 .clone()
1239 .or_else(|| self.find_numbering(NumberingType::Number)),
1240 ClassExtension::SerialComponent(r) => r
1241 .number
1242 .clone()
1243 .or_else(|| self.find_numbering(NumberingType::Number)),
1244 ClassExtension::Classic(r) => r
1245 .number
1246 .clone()
1247 .or_else(|| self.find_numbering(NumberingType::Number)),
1248 _ => self.find_numbering(NumberingType::Number),
1249 }
1250 }
1251
1252 pub fn report_number(&self) -> Option<String> {
1254 match &self.extension {
1255 ClassExtension::Monograph(_) => self.find_numbering(NumberingType::Report),
1256 _ => None,
1257 }
1258 }
1259
1260 pub fn edition(&self) -> Option<String> {
1262 match &self.extension {
1263 ClassExtension::Monograph(r) => r
1264 .edition
1265 .clone()
1266 .or_else(|| self.find_numbering(NumberingType::Edition)),
1267 ClassExtension::Collection(r) => r
1268 .edition
1269 .clone()
1270 .or_else(|| self.find_numbering(NumberingType::Edition)),
1271 ClassExtension::CollectionComponent(r) => r
1272 .edition
1273 .clone()
1274 .or_else(|| self.find_numbering(NumberingType::Edition)),
1275 ClassExtension::SerialComponent(r) => r
1276 .edition
1277 .clone()
1278 .or_else(|| self.find_numbering(NumberingType::Edition)),
1279 ClassExtension::Classic(r) => r
1280 .edition
1281 .clone()
1282 .or_else(|| self.find_numbering(NumberingType::Edition)),
1283 _ => self.find_numbering(NumberingType::Edition),
1284 }
1285 }
1286
1287 pub fn accessed(&self) -> Option<EdtfString> {
1289 class_dispatch!(&self.extension, |r| r.accessed.clone(), unknown(_) => None)
1290 }
1291
1292 pub fn unknown_fields(&self) -> Option<&std::collections::BTreeMap<String, JsonValue>> {
1298 class_dispatch!(&self.extension, |r| Some(&r.unknown_fields), unknown(_) => None)
1299 }
1300
1301 fn original_embedded(&self) -> Option<&InputReference> {
1308 let relation = match &self.extension {
1309 ClassExtension::Monograph(r) => r.original.as_ref(),
1310 ClassExtension::CollectionComponent(r) => r.original.as_ref(),
1311 ClassExtension::SerialComponent(r) => r.original.as_ref(),
1312 ClassExtension::LegalCase(r) => r.original.as_ref(),
1313 ClassExtension::Statute(r) => r.original.as_ref(),
1314 ClassExtension::Treaty(r) => r.original.as_ref(),
1315 ClassExtension::Hearing(r) => r.original.as_ref(),
1316 ClassExtension::Regulation(r) => r.original.as_ref(),
1317 ClassExtension::Brief(r) => r.original.as_ref(),
1318 ClassExtension::Classic(r) => r.original.as_ref(),
1319 ClassExtension::Patent(r) => r.original.as_ref(),
1320 ClassExtension::Dataset(r) => r.original.as_ref(),
1321 ClassExtension::Standard(r) => r.original.as_ref(),
1322 ClassExtension::Software(r) => r.original.as_ref(),
1323 ClassExtension::Event(r) => r.original.as_ref(),
1324 ClassExtension::AudioVisual(r) => r.core.original.as_ref(),
1325 _ => None,
1326 }?;
1327 match relation {
1328 WorkRelation::Embedded(p) => Some(p),
1329 WorkRelation::Id(_) => None,
1330 }
1331 }
1332
1333 pub fn original_date(&self) -> Option<EdtfString> {
1335 self.original_embedded()?.effective_issued_date()
1336 }
1337
1338 pub fn original_title(&self) -> Option<Title> {
1340 self.original_embedded()?.title()
1341 }
1342
1343 pub fn original_publisher_str(&self) -> Option<String> {
1345 self.original_embedded()?
1346 .publisher_str()
1347 .filter(|value| !value.is_empty())
1348 }
1349
1350 pub fn original_publisher_place(&self) -> Option<String> {
1352 self.original_embedded()?.publisher_place()
1353 }
1354
1355 pub fn isbn(&self) -> Option<String> {
1357 match &self.extension {
1358 ClassExtension::Monograph(r) => r.isbn.clone(),
1359 _ => None,
1360 }
1361 }
1362
1363 pub fn issn(&self) -> Option<String> {
1365 match &self.extension {
1366 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
1367 WorkRelation::Embedded(s) => s.issn(),
1368 WorkRelation::Id(_) => None,
1369 }),
1370 ClassExtension::Serial(r) => r.issn.clone(),
1371 _ => None,
1372 }
1373 }
1374
1375 pub fn keywords(&self) -> Option<Vec<String>> {
1377 match &self.extension {
1378 ClassExtension::Monograph(r) => r.keywords.clone(),
1379 ClassExtension::CollectionComponent(r) => r.keywords.clone(),
1380 ClassExtension::SerialComponent(r) => r.keywords.clone(),
1381 ClassExtension::Collection(r) => r.keywords.clone(),
1382 ClassExtension::Serial(_) => None,
1383 ClassExtension::LegalCase(r) => r.keywords.clone(),
1384 ClassExtension::Statute(r) => r.keywords.clone(),
1385 ClassExtension::Treaty(r) => r.keywords.clone(),
1386 ClassExtension::Hearing(r) => r.keywords.clone(),
1387 ClassExtension::Regulation(r) => r.keywords.clone(),
1388 ClassExtension::Brief(r) => r.keywords.clone(),
1389 ClassExtension::Classic(r) => r.keywords.clone(),
1390 ClassExtension::Patent(r) => r.keywords.clone(),
1391 ClassExtension::Dataset(r) => r.keywords.clone(),
1392 ClassExtension::Standard(r) => r.keywords.clone(),
1393 ClassExtension::Software(r) => r.keywords.clone(),
1394 ClassExtension::Event(_) => None,
1395 ClassExtension::AudioVisual(_) => None,
1396 ClassExtension::Unknown(_) => None,
1397 }
1398 }
1399
1400 pub fn language(&self) -> Option<LangID> {
1402 class_dispatch!(
1403 &self.extension,
1404 |r| r.language.clone(),
1405 audio_visual(r) => r.core.language.clone(),
1406 unknown(_) => None
1407 )
1408 }
1409
1410 pub fn field_languages(&self) -> &FieldLanguageMap {
1412 class_dispatch!(
1413 &self.extension,
1414 |r| &r.field_languages,
1415 unknown(_) => &EMPTY_FIELD_LANGUAGES
1416 )
1417 }
1418
1419 pub fn set_id(&mut self, id: impl Into<RefID>) {
1425 let id = id.into();
1426 class_dispatch!(&mut self.extension, |r| r.id = Some(id.clone()), unknown(data) => {
1427 data.fields
1428 .insert("id".to_string(), JsonValue::String(id.to_string()));
1429 });
1430 }
1431
1432 pub fn ref_type(&self) -> String {
1434 match &self.extension {
1435 ClassExtension::Monograph(r) => self.monograph_ref_type(r),
1436 ClassExtension::CollectionComponent(r) => collection_component_ref_type(r),
1437 ClassExtension::SerialComponent(r) => serial_component_ref_type(r),
1438 ClassExtension::Collection(r) => match r.r#type {
1439 CollectionType::EditedBook => "book",
1440 _ => "collection",
1441 }
1442 .to_string(),
1443 ClassExtension::Serial(r) => match r.r#type {
1444 SerialType::AcademicJournal => "article-journal",
1445 SerialType::Magazine => "article-magazine",
1446 SerialType::Newspaper => "article-newspaper",
1447 SerialType::BroadcastProgram => "broadcast",
1448 _ => "serial",
1449 }
1450 .to_string(),
1451 ClassExtension::LegalCase(_) => "legal-case".to_string(),
1452 ClassExtension::Statute(_) => "statute".to_string(),
1453 ClassExtension::Treaty(_) => "treaty".to_string(),
1454 ClassExtension::Hearing(_) => "hearing".to_string(),
1455 ClassExtension::Regulation(_) => "regulation".to_string(),
1456 ClassExtension::Brief(_) => "brief".to_string(),
1457 ClassExtension::Classic(_) => "classic".to_string(),
1458 ClassExtension::Patent(_) => "patent".to_string(),
1459 ClassExtension::Dataset(_) => "dataset".to_string(),
1460 ClassExtension::Standard(_) => "standard".to_string(),
1461 ClassExtension::Software(_) => "software".to_string(),
1462 ClassExtension::Event(r) => event_ref_type(r).to_string(),
1463 ClassExtension::AudioVisual(r) => audio_visual_ref_type(&r.r#type).to_string(),
1464 ClassExtension::Unknown(data) => {
1465 debug_assert!(
1475 !ReferenceClass::KNOWN.contains(&data.class.as_str()),
1476 "ClassExtension::Unknown should never wrap a known class string (got `{}`)",
1477 data.class
1478 );
1479 data.class.clone()
1480 }
1481 }
1482 }
1483
1484 fn monograph_ref_type(&self, r: &Monograph) -> String {
1485 match r.r#type {
1486 MonographType::Book => {
1487 if r.genre
1488 .as_deref()
1489 .is_some_and(|genre| genre.eq_ignore_ascii_case("musical_score"))
1490 {
1491 return "musical-score".to_string();
1492 }
1493 if r.genre.as_deref().is_some_and(|genre| genre == "pamphlet") {
1494 return "pamphlet".to_string();
1495 }
1496 if r.medium
1497 .as_deref()
1498 .is_some_and(|m| m.to_ascii_lowercase().contains("interview"))
1499 {
1500 "interview"
1501 } else {
1502 "book"
1503 }
1504 .to_string()
1505 }
1506 MonographType::Manual => "manual".to_string(),
1507 MonographType::Report => "report".to_string(),
1508 MonographType::Thesis => "thesis".to_string(),
1509 MonographType::Webpage => "webpage".to_string(),
1510 MonographType::Post => if r.genre.as_deref() == Some("post-weblog") {
1511 "post-weblog"
1512 } else {
1513 "post"
1514 }
1515 .to_string(),
1516 MonographType::Interview => "interview".to_string(),
1517 MonographType::Manuscript => "manuscript".to_string(),
1518 MonographType::Preprint => "preprint".to_string(),
1519 MonographType::PersonalCommunication => "personal-communication".to_string(),
1520 MonographType::Document => {
1521 if let Some(genre) = r.genre.as_deref() {
1526 let lowered = genre.to_ascii_lowercase();
1527 if matches!(
1528 lowered.as_str(),
1529 "map" | "figure" | "graphic" | "periodical" | "collection"
1530 ) {
1531 return lowered;
1532 }
1533 }
1534 if let Some(genre) = r.genre.as_deref()
1535 && matches!(genre, "bill-proceeding" | "bill-record")
1536 {
1537 return genre.to_string();
1538 }
1539 if self.genre().as_deref() == Some("conference-paper") {
1540 return "paper-conference".to_string();
1541 }
1542 if r.medium
1543 .as_deref()
1544 .is_some_and(|m| m.to_ascii_lowercase().contains("interview"))
1545 {
1546 "interview"
1547 } else {
1548 "document"
1549 }
1550 .to_string()
1551 }
1552 _ => r.r#type.as_str().to_string(),
1553 }
1554 }
1555}
1556
1557fn collection_component_ref_type(r: &CollectionComponent) -> String {
1558 match r.r#type {
1559 MonographComponentType::Chapter => match r.genre.as_deref() {
1560 Some("entry-dictionary") => "entry-dictionary",
1561 Some("entry-encyclopedia") => "entry-encyclopedia",
1562 Some("entry") => "entry",
1563 _ => "chapter",
1564 }
1565 .to_string(),
1566 MonographComponentType::Document => "paper-conference".to_string(),
1567 _ => r.r#type.as_str().to_string(),
1568 }
1569}
1570
1571fn serial_component_ref_type(r: &SerialComponent) -> String {
1572 if r.genre.as_deref() == Some("entry-encyclopedia") {
1573 return "entry-encyclopedia".to_string();
1574 }
1575 if let Some(genre) = r.genre.as_deref()
1576 && matches!(genre, "review" | "review-book")
1577 {
1578 return genre.to_string();
1579 }
1580 let container_type = r.container.as_ref().and_then(|c| match c {
1581 WorkRelation::Embedded(p) => Some(p.ref_type()),
1582 WorkRelation::Id(_) => None,
1583 });
1584 match container_type.as_deref() {
1585 Some("article-magazine") => "article-magazine".to_string(),
1586 Some("article-newspaper") => "article-newspaper".to_string(),
1587 Some("broadcast") => if r
1588 .genre
1589 .as_deref()
1590 .is_some_and(|g| g.to_ascii_lowercase().contains("film"))
1591 {
1592 "motion-picture"
1593 } else {
1594 "broadcast"
1595 }
1596 .to_string(),
1597 _ => "article-journal".to_string(),
1598 }
1599}
1600
1601fn event_ref_type(r: &Event) -> &'static str {
1602 let lowered = r.genre.as_deref().map(str::to_ascii_lowercase);
1603 match lowered.as_deref() {
1604 Some(g) if g.contains("conference") || g.contains("paper") => "paper-conference",
1605 Some(g) if g.contains("broadcast") => "broadcast",
1606 Some(g) if g.contains("performance") => "performance",
1607 Some(g) if g.contains("talk") || g.contains("speech") => "speech",
1608 _ => "event",
1609 }
1610}
1611
1612fn audio_visual_ref_type(kind: &AudioVisualType) -> &'static str {
1613 match kind {
1614 AudioVisualType::Film => "motion-picture",
1615 AudioVisualType::Episode | AudioVisualType::Broadcast => "broadcast",
1616 AudioVisualType::Recording => "song",
1617 }
1618}
1619
1620fn collect_contributors_by_role(
1625 entries: &[ContributorEntry],
1626 role: &ContributorRole,
1627) -> Option<Contributor> {
1628 let mut matches = entries
1629 .iter()
1630 .filter(|e| &e.role == role)
1631 .map(|e| &e.contributor);
1632 let first = matches.next()?;
1633 let Some(second) = matches.next() else {
1634 return Some(first.clone());
1635 };
1636 let list = std::iter::once(first)
1637 .chain(std::iter::once(second))
1638 .chain(matches)
1639 .cloned()
1640 .collect();
1641 Some(Contributor::ContributorList(ContributorList(list)))
1642}