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 _ => None,
774 }
775 }
776
777 pub fn archive(&self) -> Option<String> {
779 match &self.extension {
780 ClassExtension::Monograph(r) => r.archive.clone(),
781 _ => None,
782 }
783 }
784
785 pub fn archive_location(&self) -> Option<String> {
787 match &self.extension {
788 ClassExtension::Monograph(r) => r
789 .archive_info
790 .as_ref()
791 .and_then(|info| info.location.clone())
792 .or_else(|| r.archive_location.clone()),
793 ClassExtension::CollectionComponent(r) => {
794 r.archive_info.as_ref().and_then(|i| i.location.clone())
795 }
796 ClassExtension::SerialComponent(r) => {
797 r.archive_info.as_ref().and_then(|i| i.location.clone())
798 }
799 _ => None,
800 }
801 }
802
803 pub fn archive_name(&self) -> Option<MultilingualString> {
805 match &self.extension {
806 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.name.clone()),
807 ClassExtension::CollectionComponent(r) => {
808 r.archive_info.as_ref().and_then(|i| i.name.clone())
809 }
810 ClassExtension::SerialComponent(r) => {
811 r.archive_info.as_ref().and_then(|i| i.name.clone())
812 }
813 _ => None,
814 }
815 }
816
817 pub fn archive_place(&self) -> Option<String> {
819 match &self.extension {
820 ClassExtension::Monograph(r) => r
821 .archive_info
822 .as_ref()
823 .and_then(|i| i.place.clone().map(Into::into)),
824 ClassExtension::CollectionComponent(r) => r
825 .archive_info
826 .as_ref()
827 .and_then(|i| i.place.clone().map(Into::into)),
828 ClassExtension::SerialComponent(r) => r
829 .archive_info
830 .as_ref()
831 .and_then(|i| i.place.clone().map(Into::into)),
832 _ => None,
833 }
834 }
835
836 pub fn archive_collection(&self) -> Option<String> {
838 match &self.extension {
839 ClassExtension::Monograph(r) => {
840 r.archive_info.as_ref().and_then(|i| i.collection.clone())
841 }
842 ClassExtension::CollectionComponent(r) => {
843 r.archive_info.as_ref().and_then(|i| i.collection.clone())
844 }
845 ClassExtension::SerialComponent(r) => {
846 r.archive_info.as_ref().and_then(|i| i.collection.clone())
847 }
848 _ => None,
849 }
850 }
851
852 pub fn archive_collection_id(&self) -> Option<String> {
854 match &self.extension {
855 ClassExtension::Monograph(r) => r
856 .archive_info
857 .as_ref()
858 .and_then(|i| i.collection_id.clone()),
859 ClassExtension::CollectionComponent(r) => r
860 .archive_info
861 .as_ref()
862 .and_then(|i| i.collection_id.clone()),
863 ClassExtension::SerialComponent(r) => r
864 .archive_info
865 .as_ref()
866 .and_then(|i| i.collection_id.clone()),
867 _ => None,
868 }
869 }
870
871 pub fn archive_series(&self) -> Option<String> {
873 match &self.extension {
874 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.series.clone()),
875 ClassExtension::CollectionComponent(r) => {
876 r.archive_info.as_ref().and_then(|i| i.series.clone())
877 }
878 ClassExtension::SerialComponent(r) => {
879 r.archive_info.as_ref().and_then(|i| i.series.clone())
880 }
881 _ => None,
882 }
883 }
884
885 pub fn archive_box(&self) -> Option<String> {
887 match &self.extension {
888 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.r#box.clone()),
889 ClassExtension::CollectionComponent(r) => {
890 r.archive_info.as_ref().and_then(|i| i.r#box.clone())
891 }
892 ClassExtension::SerialComponent(r) => {
893 r.archive_info.as_ref().and_then(|i| i.r#box.clone())
894 }
895 _ => None,
896 }
897 }
898
899 pub fn archive_folder(&self) -> Option<String> {
901 match &self.extension {
902 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.folder.clone()),
903 ClassExtension::CollectionComponent(r) => {
904 r.archive_info.as_ref().and_then(|i| i.folder.clone())
905 }
906 ClassExtension::SerialComponent(r) => {
907 r.archive_info.as_ref().and_then(|i| i.folder.clone())
908 }
909 _ => None,
910 }
911 }
912
913 pub fn archive_item(&self) -> Option<String> {
915 match &self.extension {
916 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.item.clone()),
917 ClassExtension::CollectionComponent(r) => {
918 r.archive_info.as_ref().and_then(|i| i.item.clone())
919 }
920 ClassExtension::SerialComponent(r) => {
921 r.archive_info.as_ref().and_then(|i| i.item.clone())
922 }
923 _ => None,
924 }
925 }
926
927 pub fn archive_url(&self) -> Option<Url> {
929 match &self.extension {
930 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.url.clone()),
931 ClassExtension::CollectionComponent(r) => {
932 r.archive_info.as_ref().and_then(|i| i.url.clone())
933 }
934 ClassExtension::SerialComponent(r) => {
935 r.archive_info.as_ref().and_then(|i| i.url.clone())
936 }
937 _ => None,
938 }
939 }
940
941 pub fn status(&self) -> Option<String> {
943 match &self.extension {
944 ClassExtension::Monograph(r) => r.status.clone(),
945 ClassExtension::CollectionComponent(r) => r.status.clone(),
946 ClassExtension::SerialComponent(r) => r.status.clone(),
947 ClassExtension::Standard(r) => r.status.clone(),
948 _ => None,
949 }
950 }
951
952 pub fn eprint_id(&self) -> Option<String> {
954 match &self.extension {
955 ClassExtension::Monograph(r) => r.eprint.as_ref().map(|e| e.id.clone()),
956 ClassExtension::CollectionComponent(r) => r.eprint.as_ref().map(|e| e.id.clone()),
957 ClassExtension::SerialComponent(r) => r.eprint.as_ref().map(|e| e.id.clone()),
958 _ => None,
959 }
960 }
961
962 pub fn eprint_server(&self) -> Option<String> {
964 match &self.extension {
965 ClassExtension::Monograph(r) => r.eprint.as_ref().map(|e| e.server.clone()),
966 ClassExtension::CollectionComponent(r) => r.eprint.as_ref().map(|e| e.server.clone()),
967 ClassExtension::SerialComponent(r) => r.eprint.as_ref().map(|e| e.server.clone()),
968 _ => None,
969 }
970 }
971
972 pub fn eprint_class(&self) -> Option<String> {
974 match &self.extension {
975 ClassExtension::Monograph(r) => r.eprint.as_ref().and_then(|e| e.class.clone()),
976 ClassExtension::CollectionComponent(r) => {
977 r.eprint.as_ref().and_then(|e| e.class.clone())
978 }
979 ClassExtension::SerialComponent(r) => r.eprint.as_ref().and_then(|e| e.class.clone()),
980 _ => None,
981 }
982 }
983
984 pub fn medium(&self) -> Option<String> {
986 match &self.extension {
987 ClassExtension::Monograph(r) => {
988 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
989 }
990 ClassExtension::CollectionComponent(r) => {
991 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
992 }
993 ClassExtension::SerialComponent(r) => {
994 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
995 }
996 ClassExtension::AudioVisual(r) => {
997 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
998 }
999 _ => None,
1000 }
1001 }
1002
1003 pub fn version(&self) -> Option<String> {
1005 match &self.extension {
1006 ClassExtension::Dataset(r) => r.version.clone(),
1007 ClassExtension::Software(r) => r.version.clone(),
1008 _ => None,
1009 }
1010 }
1011
1012 pub fn abstract_text(&self) -> Option<RichText> {
1014 match &self.extension {
1015 ClassExtension::Monograph(r) => r.abstract_text.clone(),
1016 ClassExtension::CollectionComponent(r) => r.abstract_text.clone(),
1017 ClassExtension::SerialComponent(r) => r.abstract_text.clone(),
1018 _ => None,
1019 }
1020 }
1021
1022 pub fn container_title(&self) -> Option<Title> {
1024 match &self.extension {
1025 ClassExtension::Monograph(r) => r.container.as_ref().and_then(|c| match c {
1026 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1027 WorkRelation::Id(_) => None,
1028 }),
1029 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
1030 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1031 WorkRelation::Id(_) => None,
1032 }),
1033 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
1034 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1035 WorkRelation::Id(_) => None,
1036 }),
1037 ClassExtension::Serial(r) => r.container.as_ref().and_then(|c| match c {
1038 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1039 WorkRelation::Id(_) => None,
1040 }),
1041 ClassExtension::LegalCase(r) => r.reporter.clone().map(Title::Single),
1042 ClassExtension::Statute(r) => r.code.clone().map(Title::Single),
1043 ClassExtension::Treaty(r) => r.reporter.clone().map(Title::Single),
1044 ClassExtension::Event(r) => r.container.as_ref().and_then(|c| match c {
1045 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1046 WorkRelation::Id(_) => None,
1047 }),
1048 ClassExtension::AudioVisual(r) => r.container.as_ref().and_then(|c| match c {
1049 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1050 WorkRelation::Id(_) => None,
1051 }),
1052 _ => None,
1053 }
1054 }
1055
1056 pub fn collection_title(&self) -> Option<Title> {
1058 fn nested_collection_title(relation: &WorkRelation) -> Option<Title> {
1059 let WorkRelation::Embedded(parent) = relation else {
1060 return None;
1061 };
1062 let container = match parent.extension() {
1063 ClassExtension::Monograph(r) => r.container.as_ref(),
1064 ClassExtension::Collection(r) => r.container.as_ref(),
1065 ClassExtension::Serial(r) => r.container.as_ref(),
1066 _ => None,
1067 }?;
1068 match container {
1069 WorkRelation::Embedded(collection) => collection.title(),
1070 WorkRelation::Id(_) => None,
1071 }
1072 }
1073
1074 match &self.extension {
1075 ClassExtension::Monograph(r) => r.container.as_ref().and_then(nested_collection_title),
1076 ClassExtension::CollectionComponent(r) => {
1077 r.container.as_ref().and_then(nested_collection_title)
1078 }
1079 ClassExtension::SerialComponent(r) => {
1080 r.container.as_ref().and_then(nested_collection_title)
1081 }
1082 _ => None,
1083 }
1084 }
1085
1086 pub fn volume(&self) -> Option<NumOrStr> {
1088 match &self.extension {
1089 ClassExtension::Monograph(r) => r
1090 .volume
1091 .clone()
1092 .or_else(|| self.find_numbering(NumberingType::Volume))
1093 .map(NumOrStr::Str),
1094 ClassExtension::Collection(r) => r
1095 .volume
1096 .clone()
1097 .or_else(|| self.find_numbering(NumberingType::Volume))
1098 .map(NumOrStr::Str),
1099 ClassExtension::CollectionComponent(r) => r
1100 .volume
1101 .clone()
1102 .or_else(|| self.find_numbering(NumberingType::Volume))
1103 .map(NumOrStr::Str),
1104 ClassExtension::SerialComponent(r) => r
1105 .volume
1106 .clone()
1107 .or_else(|| self.find_numbering(NumberingType::Volume))
1108 .map(NumOrStr::Str),
1109 ClassExtension::Classic(r) => r
1110 .volume
1111 .clone()
1112 .or_else(|| self.find_numbering(NumberingType::Volume))
1113 .map(NumOrStr::Str),
1114 ClassExtension::LegalCase(r) => r.volume.clone().map(NumOrStr::Str),
1115 ClassExtension::Statute(r) => r.volume.clone().map(NumOrStr::Str),
1116 ClassExtension::Treaty(r) => r.volume.clone().map(NumOrStr::Str),
1117 ClassExtension::Regulation(r) => r.volume.clone().map(NumOrStr::Str),
1118 _ => self
1119 .find_numbering(NumberingType::Volume)
1120 .map(NumOrStr::Str),
1121 }
1122 }
1123
1124 pub fn collection_number(&self) -> Option<String> {
1126 match &self.extension {
1127 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
1128 WorkRelation::Embedded(p) => p.collection_number(),
1129 WorkRelation::Id(_) => None,
1130 }),
1131 _ => self.find_numbering(NumberingType::Volume),
1132 }
1133 }
1134
1135 pub fn issue(&self) -> Option<NumOrStr> {
1137 match &self.extension {
1138 ClassExtension::Monograph(r) => r
1139 .issue
1140 .clone()
1141 .or_else(|| self.find_numbering(NumberingType::Issue))
1142 .map(NumOrStr::Str),
1143 ClassExtension::Collection(r) => r
1144 .issue
1145 .clone()
1146 .or_else(|| self.find_numbering(NumberingType::Issue))
1147 .map(NumOrStr::Str),
1148 ClassExtension::CollectionComponent(r) => r
1149 .issue
1150 .clone()
1151 .or_else(|| self.find_numbering(NumberingType::Issue))
1152 .map(NumOrStr::Str),
1153 ClassExtension::SerialComponent(r) => r
1154 .issue
1155 .clone()
1156 .or_else(|| self.find_numbering(NumberingType::Issue))
1157 .map(NumOrStr::Str),
1158 ClassExtension::Classic(r) => r
1159 .issue
1160 .clone()
1161 .or_else(|| self.find_numbering(NumberingType::Issue))
1162 .map(NumOrStr::Str),
1163 _ => self.find_numbering(NumberingType::Issue).map(NumOrStr::Str),
1164 }
1165 }
1166
1167 pub fn pages(&self) -> Option<NumOrStr> {
1169 match &self.extension {
1170 ClassExtension::CollectionComponent(r) => r.pages.clone(),
1171 ClassExtension::SerialComponent(r) => r.pages.clone().map(NumOrStr::Str),
1172 ClassExtension::LegalCase(r) => r.page.clone().map(NumOrStr::Str),
1173 ClassExtension::Statute(r) => r.page.clone().map(NumOrStr::Str),
1174 ClassExtension::Treaty(r) => r.page.clone().map(NumOrStr::Str),
1175 _ => None,
1176 }
1177 }
1178
1179 pub fn authority(&self) -> Option<String> {
1181 match &self.extension {
1182 ClassExtension::LegalCase(r) => r.authority.clone(),
1183 ClassExtension::Statute(r) => r.authority.clone(),
1184 ClassExtension::Hearing(r) => r.authority.clone(),
1185 ClassExtension::Regulation(r) => r.authority.clone(),
1186 ClassExtension::Brief(r) => r.authority.clone(),
1187 ClassExtension::Patent(r) => r.authority.clone(),
1188 ClassExtension::Standard(r) => r.authority.clone(),
1189 _ => None,
1190 }
1191 }
1192
1193 pub fn reporter(&self) -> Option<String> {
1195 match &self.extension {
1196 ClassExtension::LegalCase(r) => r.reporter.clone(),
1197 ClassExtension::Treaty(r) => r.reporter.clone(),
1198 _ => None,
1199 }
1200 }
1201
1202 pub fn code(&self) -> Option<String> {
1204 match &self.extension {
1205 ClassExtension::Statute(r) => r.code.clone(),
1206 ClassExtension::Regulation(r) => r.code.clone(),
1207 _ => None,
1208 }
1209 }
1210
1211 pub fn section(&self) -> Option<String> {
1213 match &self.extension {
1214 ClassExtension::Statute(r) => r.section.clone(),
1215 ClassExtension::Regulation(r) => r.section.clone(),
1216 ClassExtension::Classic(_) => self.find_numbering(NumberingType::Section),
1217 _ => None,
1218 }
1219 }
1220
1221 pub fn number(&self) -> Option<String> {
1223 match &self.extension {
1224 ClassExtension::Monograph(r) => r
1225 .number
1226 .clone()
1227 .or_else(|| self.find_numbering(NumberingType::Number)),
1228 ClassExtension::Statute(r) => r.number.clone(),
1229 ClassExtension::Hearing(r) => r.session_number.clone(),
1230 ClassExtension::Collection(r) => r
1231 .number
1232 .clone()
1233 .or_else(|| self.find_numbering(NumberingType::Number)),
1234 ClassExtension::CollectionComponent(r) => r
1235 .number
1236 .clone()
1237 .or_else(|| self.find_numbering(NumberingType::Number)),
1238 ClassExtension::SerialComponent(r) => r
1239 .number
1240 .clone()
1241 .or_else(|| self.find_numbering(NumberingType::Number)),
1242 ClassExtension::Classic(r) => r
1243 .number
1244 .clone()
1245 .or_else(|| self.find_numbering(NumberingType::Number)),
1246 _ => self.find_numbering(NumberingType::Number),
1247 }
1248 }
1249
1250 pub fn report_number(&self) -> Option<String> {
1252 match &self.extension {
1253 ClassExtension::Monograph(_) => self.find_numbering(NumberingType::Report),
1254 _ => None,
1255 }
1256 }
1257
1258 pub fn edition(&self) -> Option<String> {
1260 match &self.extension {
1261 ClassExtension::Monograph(r) => r
1262 .edition
1263 .clone()
1264 .or_else(|| self.find_numbering(NumberingType::Edition)),
1265 ClassExtension::Collection(r) => r
1266 .edition
1267 .clone()
1268 .or_else(|| self.find_numbering(NumberingType::Edition)),
1269 ClassExtension::CollectionComponent(r) => r
1270 .edition
1271 .clone()
1272 .or_else(|| self.find_numbering(NumberingType::Edition)),
1273 ClassExtension::SerialComponent(r) => r
1274 .edition
1275 .clone()
1276 .or_else(|| self.find_numbering(NumberingType::Edition)),
1277 ClassExtension::Classic(r) => r
1278 .edition
1279 .clone()
1280 .or_else(|| self.find_numbering(NumberingType::Edition)),
1281 _ => self.find_numbering(NumberingType::Edition),
1282 }
1283 }
1284
1285 pub fn accessed(&self) -> Option<EdtfString> {
1287 class_dispatch!(&self.extension, |r| r.accessed.clone(), unknown(_) => None)
1288 }
1289
1290 pub fn unknown_fields(&self) -> Option<&std::collections::BTreeMap<String, JsonValue>> {
1296 class_dispatch!(&self.extension, |r| Some(&r.unknown_fields), unknown(_) => None)
1297 }
1298
1299 fn original_embedded(&self) -> Option<&InputReference> {
1306 let relation = match &self.extension {
1307 ClassExtension::Monograph(r) => r.original.as_ref(),
1308 ClassExtension::CollectionComponent(r) => r.original.as_ref(),
1309 ClassExtension::SerialComponent(r) => r.original.as_ref(),
1310 ClassExtension::LegalCase(r) => r.original.as_ref(),
1311 ClassExtension::Statute(r) => r.original.as_ref(),
1312 ClassExtension::Treaty(r) => r.original.as_ref(),
1313 ClassExtension::Hearing(r) => r.original.as_ref(),
1314 ClassExtension::Regulation(r) => r.original.as_ref(),
1315 ClassExtension::Brief(r) => r.original.as_ref(),
1316 ClassExtension::Classic(r) => r.original.as_ref(),
1317 ClassExtension::Patent(r) => r.original.as_ref(),
1318 ClassExtension::Dataset(r) => r.original.as_ref(),
1319 ClassExtension::Standard(r) => r.original.as_ref(),
1320 ClassExtension::Software(r) => r.original.as_ref(),
1321 ClassExtension::Event(r) => r.original.as_ref(),
1322 ClassExtension::AudioVisual(r) => r.core.original.as_ref(),
1323 _ => None,
1324 }?;
1325 match relation {
1326 WorkRelation::Embedded(p) => Some(p),
1327 WorkRelation::Id(_) => None,
1328 }
1329 }
1330
1331 pub fn original_date(&self) -> Option<EdtfString> {
1333 self.original_embedded()?.effective_issued_date()
1334 }
1335
1336 pub fn original_title(&self) -> Option<Title> {
1338 self.original_embedded()?.title()
1339 }
1340
1341 pub fn original_publisher_str(&self) -> Option<String> {
1343 self.original_embedded()?
1344 .publisher_str()
1345 .filter(|value| !value.is_empty())
1346 }
1347
1348 pub fn original_publisher_place(&self) -> Option<String> {
1350 self.original_embedded()?.publisher_place()
1351 }
1352
1353 pub fn isbn(&self) -> Option<String> {
1355 match &self.extension {
1356 ClassExtension::Monograph(r) => r.isbn.clone(),
1357 _ => None,
1358 }
1359 }
1360
1361 pub fn issn(&self) -> Option<String> {
1363 match &self.extension {
1364 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
1365 WorkRelation::Embedded(s) => s.issn(),
1366 WorkRelation::Id(_) => None,
1367 }),
1368 ClassExtension::Serial(r) => r.issn.clone(),
1369 _ => None,
1370 }
1371 }
1372
1373 pub fn keywords(&self) -> Option<Vec<String>> {
1375 match &self.extension {
1376 ClassExtension::Monograph(r) => r.keywords.clone(),
1377 ClassExtension::CollectionComponent(r) => r.keywords.clone(),
1378 ClassExtension::SerialComponent(r) => r.keywords.clone(),
1379 ClassExtension::Collection(r) => r.keywords.clone(),
1380 ClassExtension::Serial(_) => None,
1381 ClassExtension::LegalCase(r) => r.keywords.clone(),
1382 ClassExtension::Statute(r) => r.keywords.clone(),
1383 ClassExtension::Treaty(r) => r.keywords.clone(),
1384 ClassExtension::Hearing(r) => r.keywords.clone(),
1385 ClassExtension::Regulation(r) => r.keywords.clone(),
1386 ClassExtension::Brief(r) => r.keywords.clone(),
1387 ClassExtension::Classic(r) => r.keywords.clone(),
1388 ClassExtension::Patent(r) => r.keywords.clone(),
1389 ClassExtension::Dataset(r) => r.keywords.clone(),
1390 ClassExtension::Standard(r) => r.keywords.clone(),
1391 ClassExtension::Software(r) => r.keywords.clone(),
1392 ClassExtension::Event(_) => None,
1393 ClassExtension::AudioVisual(_) => None,
1394 ClassExtension::Unknown(_) => None,
1395 }
1396 }
1397
1398 pub fn language(&self) -> Option<LangID> {
1400 class_dispatch!(
1401 &self.extension,
1402 |r| r.language.clone(),
1403 audio_visual(r) => r.core.language.clone(),
1404 unknown(_) => None
1405 )
1406 }
1407
1408 pub fn field_languages(&self) -> &FieldLanguageMap {
1410 class_dispatch!(
1411 &self.extension,
1412 |r| &r.field_languages,
1413 unknown(_) => &EMPTY_FIELD_LANGUAGES
1414 )
1415 }
1416
1417 pub fn set_id(&mut self, id: impl Into<RefID>) {
1423 let id = id.into();
1424 class_dispatch!(&mut self.extension, |r| r.id = Some(id.clone()), unknown(data) => {
1425 data.fields
1426 .insert("id".to_string(), JsonValue::String(id.to_string()));
1427 });
1428 }
1429
1430 pub fn ref_type(&self) -> String {
1432 match &self.extension {
1433 ClassExtension::Monograph(r) => self.monograph_ref_type(r),
1434 ClassExtension::CollectionComponent(r) => collection_component_ref_type(r),
1435 ClassExtension::SerialComponent(r) => serial_component_ref_type(r),
1436 ClassExtension::Collection(r) => match r.r#type {
1437 CollectionType::EditedBook => "book",
1438 _ => "collection",
1439 }
1440 .to_string(),
1441 ClassExtension::Serial(r) => match r.r#type {
1442 SerialType::AcademicJournal => "article-journal",
1443 SerialType::Magazine => "article-magazine",
1444 SerialType::Newspaper => "article-newspaper",
1445 SerialType::BroadcastProgram => "broadcast",
1446 _ => "serial",
1447 }
1448 .to_string(),
1449 ClassExtension::LegalCase(_) => "legal-case".to_string(),
1450 ClassExtension::Statute(_) => "statute".to_string(),
1451 ClassExtension::Treaty(_) => "treaty".to_string(),
1452 ClassExtension::Hearing(_) => "hearing".to_string(),
1453 ClassExtension::Regulation(_) => "regulation".to_string(),
1454 ClassExtension::Brief(_) => "brief".to_string(),
1455 ClassExtension::Classic(_) => "classic".to_string(),
1456 ClassExtension::Patent(_) => "patent".to_string(),
1457 ClassExtension::Dataset(_) => "dataset".to_string(),
1458 ClassExtension::Standard(_) => "standard".to_string(),
1459 ClassExtension::Software(_) => "software".to_string(),
1460 ClassExtension::Event(r) => event_ref_type(r).to_string(),
1461 ClassExtension::AudioVisual(r) => audio_visual_ref_type(&r.r#type).to_string(),
1462 ClassExtension::Unknown(data) => {
1463 debug_assert!(
1473 !ReferenceClass::KNOWN.contains(&data.class.as_str()),
1474 "ClassExtension::Unknown should never wrap a known class string (got `{}`)",
1475 data.class
1476 );
1477 data.class.clone()
1478 }
1479 }
1480 }
1481
1482 fn monograph_ref_type(&self, r: &Monograph) -> String {
1483 match r.r#type {
1484 MonographType::Book => if r
1485 .medium
1486 .as_deref()
1487 .is_some_and(|m| m.to_ascii_lowercase().contains("interview"))
1488 {
1489 "interview"
1490 } else {
1491 "book"
1492 }
1493 .to_string(),
1494 MonographType::Manual => "manual".to_string(),
1495 MonographType::Report => "report".to_string(),
1496 MonographType::Thesis => "thesis".to_string(),
1497 MonographType::Webpage => "webpage".to_string(),
1498 MonographType::Post => "post".to_string(),
1499 MonographType::Interview => "interview".to_string(),
1500 MonographType::Manuscript => "manuscript".to_string(),
1501 MonographType::Preprint => "preprint".to_string(),
1502 MonographType::PersonalCommunication => "personal-communication".to_string(),
1503 MonographType::Document => {
1504 if r.genre
1505 .as_deref()
1506 .is_some_and(|genre| genre.eq_ignore_ascii_case("map"))
1507 {
1508 return "map".to_string();
1509 }
1510 if let Some(genre) = r.genre.as_deref()
1511 && matches!(genre, "bill-proceeding" | "bill-record")
1512 {
1513 return genre.to_string();
1514 }
1515 if self.genre().as_deref() == Some("conference-paper") {
1516 return "paper-conference".to_string();
1517 }
1518 if r.medium
1519 .as_deref()
1520 .is_some_and(|m| m.to_ascii_lowercase().contains("interview"))
1521 {
1522 "interview"
1523 } else {
1524 "document"
1525 }
1526 .to_string()
1527 }
1528 _ => r.r#type.as_str().to_string(),
1529 }
1530 }
1531}
1532
1533fn collection_component_ref_type(r: &CollectionComponent) -> String {
1534 match r.r#type {
1535 MonographComponentType::Chapter => match r.genre.as_deref() {
1536 Some("entry-dictionary") => "entry-dictionary",
1537 Some("entry-encyclopedia") => "entry-encyclopedia",
1538 _ => "chapter",
1539 }
1540 .to_string(),
1541 MonographComponentType::Document => "paper-conference".to_string(),
1542 _ => r.r#type.as_str().to_string(),
1543 }
1544}
1545
1546fn serial_component_ref_type(r: &SerialComponent) -> String {
1547 if r.genre.as_deref() == Some("entry-encyclopedia") {
1548 return "entry-encyclopedia".to_string();
1549 }
1550 let container_type = r.container.as_ref().and_then(|c| match c {
1551 WorkRelation::Embedded(p) => Some(p.ref_type()),
1552 WorkRelation::Id(_) => None,
1553 });
1554 match container_type.as_deref() {
1555 Some("article-magazine") => "article-magazine".to_string(),
1556 Some("article-newspaper") => "article-newspaper".to_string(),
1557 Some("broadcast") => if r
1558 .genre
1559 .as_deref()
1560 .is_some_and(|g| g.to_ascii_lowercase().contains("film"))
1561 {
1562 "motion-picture"
1563 } else {
1564 "broadcast"
1565 }
1566 .to_string(),
1567 _ => "article-journal".to_string(),
1568 }
1569}
1570
1571fn event_ref_type(r: &Event) -> &'static str {
1572 let lowered = r.genre.as_deref().map(str::to_ascii_lowercase);
1573 match lowered.as_deref() {
1574 Some(g) if g.contains("conference") || g.contains("paper") => "paper-conference",
1575 Some(g) if g.contains("broadcast") => "broadcast",
1576 Some(g) if g.contains("talk") || g.contains("speech") => "speech",
1577 _ => "event",
1578 }
1579}
1580
1581fn audio_visual_ref_type(kind: &AudioVisualType) -> &'static str {
1582 match kind {
1583 AudioVisualType::Film => "motion-picture",
1584 AudioVisualType::Episode | AudioVisualType::Broadcast => "broadcast",
1585 AudioVisualType::Recording => "song",
1586 }
1587}
1588
1589fn collect_contributors_by_role(
1594 entries: &[ContributorEntry],
1595 role: &ContributorRole,
1596) -> Option<Contributor> {
1597 let mut matches = entries
1598 .iter()
1599 .filter(|e| &e.role == role)
1600 .map(|e| &e.contributor);
1601 let first = matches.next()?;
1602 let Some(second) = matches.next() else {
1603 return Some(first.clone());
1604 };
1605 let list = std::iter::once(first)
1606 .chain(std::iter::once(second))
1607 .chain(matches)
1608 .cloned()
1609 .collect();
1610 Some(Contributor::ContributorList(ContributorList(list)))
1611}