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 csl_issued_date(&self) -> Option<EdtfString> {
572 self.issued().or_else(|| self.created())
573 }
574
575 pub fn doi(&self) -> Option<String> {
577 match &self.extension {
578 ClassExtension::Monograph(r) => r.doi.clone(),
579 ClassExtension::CollectionComponent(r) => r.doi.clone(),
580 ClassExtension::SerialComponent(r) => r.doi.clone(),
581 ClassExtension::LegalCase(r) => r.doi.clone(),
582 ClassExtension::Dataset(r) => r.doi.clone(),
583 ClassExtension::Software(r) => r.doi.clone(),
584 ClassExtension::AudioVisual(_) => None,
585 _ => None,
586 }
587 }
588
589 pub fn ads_bibcode(&self) -> Option<String> {
591 match &self.extension {
592 ClassExtension::Monograph(r) => r.ads_bibcode.clone(),
593 ClassExtension::SerialComponent(r) => r.ads_bibcode.clone(),
594 ClassExtension::AudioVisual(_) => None,
595 _ => None,
596 }
597 }
598
599 pub fn note(&self) -> Option<RichText> {
601 match &self.extension {
602 ClassExtension::Monograph(r) => r.note.clone(),
603 ClassExtension::CollectionComponent(r) => r.note.clone(),
604 ClassExtension::SerialComponent(r) => r.note.clone(),
605 ClassExtension::Collection(r) => r.note.clone(),
606 ClassExtension::Serial(r) => r.note.clone(),
607 ClassExtension::LegalCase(r) => r.note.clone(),
608 ClassExtension::Statute(r) => r.note.clone(),
609 ClassExtension::Treaty(r) => r.note.clone(),
610 ClassExtension::Standard(r) => r.note.clone(),
611 ClassExtension::Event(r) => r.note.clone(),
612 ClassExtension::AudioVisual(r) => r.note.clone(),
613 _ => None,
614 }
615 }
616
617 pub fn url(&self) -> Option<Url> {
619 match &self.extension {
620 ClassExtension::Monograph(r) => r.url.clone(),
621 ClassExtension::CollectionComponent(r) => r.url.clone(),
622 ClassExtension::SerialComponent(r) => r.url.clone(),
623 ClassExtension::Collection(r) => r.url.clone(),
624 ClassExtension::Serial(r) => r.url.clone(),
625 ClassExtension::LegalCase(r) => r.url.clone(),
626 ClassExtension::Statute(r) => r.url.clone(),
627 ClassExtension::Treaty(r) => r.url.clone(),
628 ClassExtension::Hearing(r) => r.url.clone(),
629 ClassExtension::Regulation(r) => r.url.clone(),
630 ClassExtension::Brief(r) => r.url.clone(),
631 ClassExtension::Classic(r) => r.url.clone(),
632 ClassExtension::Patent(r) => r.url.clone(),
633 ClassExtension::Dataset(r) => r.url.clone(),
634 ClassExtension::Standard(r) => r.url.clone(),
635 ClassExtension::Software(r) => r.url.clone(),
636 ClassExtension::Event(r) => r.url.clone(),
637 ClassExtension::AudioVisual(r) => r.url.clone(),
638 ClassExtension::Unknown(_) => None,
639 }
640 }
641
642 pub fn publisher_place(&self) -> Option<String> {
644 match &self.extension {
645 ClassExtension::Monograph(r) => r
646 .publisher
647 .as_ref()
648 .and_then(|p| p.place.clone().map(Into::into))
649 .or_else(|| {
650 r.container.as_ref().and_then(|c| match c {
651 WorkRelation::Embedded(p) => p.publisher_place(),
652 _ => None,
653 })
654 }),
655 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
656 WorkRelation::Embedded(p) => p.publisher_place(),
657 _ => None,
658 }),
659 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
660 WorkRelation::Embedded(p) => p.publisher_place(),
661 _ => None,
662 }),
663 ClassExtension::Collection(r) => r
664 .publisher
665 .as_ref()
666 .and_then(|p| p.place.clone().map(Into::into))
667 .or_else(|| {
668 r.container.as_ref().and_then(|c| match c {
669 WorkRelation::Embedded(p) => p.publisher_place(),
670 _ => None,
671 })
672 }),
673 ClassExtension::Serial(_) => None,
674 ClassExtension::Classic(r) => r
675 .publisher
676 .as_ref()
677 .and_then(|p| p.place.clone().map(Into::into)),
678 ClassExtension::Dataset(r) => r
679 .publisher
680 .as_ref()
681 .and_then(|p| p.place.clone().map(Into::into)),
682 ClassExtension::Standard(r) => r
683 .publisher
684 .as_ref()
685 .and_then(|p| p.place.clone().map(Into::into)),
686 ClassExtension::Software(r) => r
687 .publisher
688 .as_ref()
689 .and_then(|p| p.place.clone().map(Into::into)),
690 ClassExtension::Event(r) => r.location.clone(),
691 ClassExtension::AudioVisual(r) => r
692 .publisher
693 .as_ref()
694 .and_then(|p| p.place.clone().map(Into::into)),
695 _ => None,
696 }
697 }
698
699 pub fn publisher_str(&self) -> Option<String> {
701 match &self.extension {
702 ClassExtension::Monograph(r) => r
703 .publisher
704 .as_ref()
705 .map(|p| p.name.to_string())
706 .or_else(|| {
707 r.container.as_ref().and_then(|c| match c {
708 WorkRelation::Embedded(p) => p.publisher_str(),
709 _ => None,
710 })
711 }),
712 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
713 WorkRelation::Embedded(p) => p.publisher_str(),
714 _ => None,
715 }),
716 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
717 WorkRelation::Embedded(p) => p.publisher_str(),
718 _ => None,
719 }),
720 ClassExtension::Collection(r) => r
721 .publisher
722 .as_ref()
723 .map(|p| p.name.to_string())
724 .or_else(|| {
725 r.container.as_ref().and_then(|c| match c {
726 WorkRelation::Embedded(p) => p.publisher_str(),
727 _ => None,
728 })
729 }),
730 ClassExtension::Serial(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
731 ClassExtension::Classic(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
732 ClassExtension::Dataset(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
733 ClassExtension::Standard(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
734 ClassExtension::Software(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
735 ClassExtension::Event(r) => r.network.clone(),
736 ClassExtension::AudioVisual(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
737 _ => None,
738 }
739 }
740
741 pub(super) fn normalize_genre_medium(s: &str) -> String {
745 let lower = s.to_ascii_lowercase();
746 lower
747 .split(|c: char| c.is_whitespace() || c == '_')
748 .filter(|p| !p.is_empty())
749 .collect::<Vec<_>>()
750 .join("-")
751 }
752
753 pub fn genre(&self) -> Option<String> {
755 match &self.extension {
756 ClassExtension::Monograph(r) => {
757 r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
758 }
759 ClassExtension::CollectionComponent(r) => {
760 r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
761 }
762 ClassExtension::SerialComponent(r) => {
763 r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
764 }
765 ClassExtension::Event(r) => r.genre.as_ref().map(|g| Self::normalize_genre_medium(g)),
766 ClassExtension::AudioVisual(r) => r
767 .core
768 .genre
769 .as_ref()
770 .map(|g| Self::normalize_genre_medium(g)),
771 _ => None,
772 }
773 }
774
775 pub fn archive(&self) -> Option<String> {
777 match &self.extension {
778 ClassExtension::Monograph(r) => r.archive.clone(),
779 _ => None,
780 }
781 }
782
783 pub fn archive_location(&self) -> Option<String> {
785 match &self.extension {
786 ClassExtension::Monograph(r) => r
787 .archive_info
788 .as_ref()
789 .and_then(|info| info.location.clone())
790 .or_else(|| r.archive_location.clone()),
791 ClassExtension::CollectionComponent(r) => {
792 r.archive_info.as_ref().and_then(|i| i.location.clone())
793 }
794 ClassExtension::SerialComponent(r) => {
795 r.archive_info.as_ref().and_then(|i| i.location.clone())
796 }
797 _ => None,
798 }
799 }
800
801 pub fn archive_name(&self) -> Option<MultilingualString> {
803 match &self.extension {
804 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.name.clone()),
805 ClassExtension::CollectionComponent(r) => {
806 r.archive_info.as_ref().and_then(|i| i.name.clone())
807 }
808 ClassExtension::SerialComponent(r) => {
809 r.archive_info.as_ref().and_then(|i| i.name.clone())
810 }
811 _ => None,
812 }
813 }
814
815 pub fn archive_place(&self) -> Option<String> {
817 match &self.extension {
818 ClassExtension::Monograph(r) => r
819 .archive_info
820 .as_ref()
821 .and_then(|i| i.place.clone().map(Into::into)),
822 ClassExtension::CollectionComponent(r) => r
823 .archive_info
824 .as_ref()
825 .and_then(|i| i.place.clone().map(Into::into)),
826 ClassExtension::SerialComponent(r) => r
827 .archive_info
828 .as_ref()
829 .and_then(|i| i.place.clone().map(Into::into)),
830 _ => None,
831 }
832 }
833
834 pub fn archive_collection(&self) -> Option<String> {
836 match &self.extension {
837 ClassExtension::Monograph(r) => {
838 r.archive_info.as_ref().and_then(|i| i.collection.clone())
839 }
840 ClassExtension::CollectionComponent(r) => {
841 r.archive_info.as_ref().and_then(|i| i.collection.clone())
842 }
843 ClassExtension::SerialComponent(r) => {
844 r.archive_info.as_ref().and_then(|i| i.collection.clone())
845 }
846 _ => None,
847 }
848 }
849
850 pub fn archive_collection_id(&self) -> Option<String> {
852 match &self.extension {
853 ClassExtension::Monograph(r) => r
854 .archive_info
855 .as_ref()
856 .and_then(|i| i.collection_id.clone()),
857 ClassExtension::CollectionComponent(r) => r
858 .archive_info
859 .as_ref()
860 .and_then(|i| i.collection_id.clone()),
861 ClassExtension::SerialComponent(r) => r
862 .archive_info
863 .as_ref()
864 .and_then(|i| i.collection_id.clone()),
865 _ => None,
866 }
867 }
868
869 pub fn archive_series(&self) -> Option<String> {
871 match &self.extension {
872 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.series.clone()),
873 ClassExtension::CollectionComponent(r) => {
874 r.archive_info.as_ref().and_then(|i| i.series.clone())
875 }
876 ClassExtension::SerialComponent(r) => {
877 r.archive_info.as_ref().and_then(|i| i.series.clone())
878 }
879 _ => None,
880 }
881 }
882
883 pub fn archive_box(&self) -> Option<String> {
885 match &self.extension {
886 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.r#box.clone()),
887 ClassExtension::CollectionComponent(r) => {
888 r.archive_info.as_ref().and_then(|i| i.r#box.clone())
889 }
890 ClassExtension::SerialComponent(r) => {
891 r.archive_info.as_ref().and_then(|i| i.r#box.clone())
892 }
893 _ => None,
894 }
895 }
896
897 pub fn archive_folder(&self) -> Option<String> {
899 match &self.extension {
900 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.folder.clone()),
901 ClassExtension::CollectionComponent(r) => {
902 r.archive_info.as_ref().and_then(|i| i.folder.clone())
903 }
904 ClassExtension::SerialComponent(r) => {
905 r.archive_info.as_ref().and_then(|i| i.folder.clone())
906 }
907 _ => None,
908 }
909 }
910
911 pub fn archive_item(&self) -> Option<String> {
913 match &self.extension {
914 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.item.clone()),
915 ClassExtension::CollectionComponent(r) => {
916 r.archive_info.as_ref().and_then(|i| i.item.clone())
917 }
918 ClassExtension::SerialComponent(r) => {
919 r.archive_info.as_ref().and_then(|i| i.item.clone())
920 }
921 _ => None,
922 }
923 }
924
925 pub fn archive_url(&self) -> Option<Url> {
927 match &self.extension {
928 ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.url.clone()),
929 ClassExtension::CollectionComponent(r) => {
930 r.archive_info.as_ref().and_then(|i| i.url.clone())
931 }
932 ClassExtension::SerialComponent(r) => {
933 r.archive_info.as_ref().and_then(|i| i.url.clone())
934 }
935 _ => None,
936 }
937 }
938
939 pub fn status(&self) -> Option<String> {
941 match &self.extension {
942 ClassExtension::Monograph(r) => r.status.clone(),
943 ClassExtension::CollectionComponent(r) => r.status.clone(),
944 ClassExtension::SerialComponent(r) => r.status.clone(),
945 ClassExtension::Standard(r) => r.status.clone(),
946 _ => None,
947 }
948 }
949
950 pub fn eprint_id(&self) -> Option<String> {
952 match &self.extension {
953 ClassExtension::Monograph(r) => r.eprint.as_ref().map(|e| e.id.clone()),
954 ClassExtension::CollectionComponent(r) => r.eprint.as_ref().map(|e| e.id.clone()),
955 ClassExtension::SerialComponent(r) => r.eprint.as_ref().map(|e| e.id.clone()),
956 _ => None,
957 }
958 }
959
960 pub fn eprint_server(&self) -> Option<String> {
962 match &self.extension {
963 ClassExtension::Monograph(r) => r.eprint.as_ref().map(|e| e.server.clone()),
964 ClassExtension::CollectionComponent(r) => r.eprint.as_ref().map(|e| e.server.clone()),
965 ClassExtension::SerialComponent(r) => r.eprint.as_ref().map(|e| e.server.clone()),
966 _ => None,
967 }
968 }
969
970 pub fn eprint_class(&self) -> Option<String> {
972 match &self.extension {
973 ClassExtension::Monograph(r) => r.eprint.as_ref().and_then(|e| e.class.clone()),
974 ClassExtension::CollectionComponent(r) => {
975 r.eprint.as_ref().and_then(|e| e.class.clone())
976 }
977 ClassExtension::SerialComponent(r) => r.eprint.as_ref().and_then(|e| e.class.clone()),
978 _ => None,
979 }
980 }
981
982 pub fn medium(&self) -> Option<String> {
984 match &self.extension {
985 ClassExtension::Monograph(r) => {
986 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
987 }
988 ClassExtension::CollectionComponent(r) => {
989 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
990 }
991 ClassExtension::SerialComponent(r) => {
992 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
993 }
994 ClassExtension::AudioVisual(r) => {
995 r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
996 }
997 _ => None,
998 }
999 }
1000
1001 pub fn version(&self) -> Option<String> {
1003 match &self.extension {
1004 ClassExtension::Dataset(r) => r.version.clone(),
1005 ClassExtension::Software(r) => r.version.clone(),
1006 _ => None,
1007 }
1008 }
1009
1010 pub fn abstract_text(&self) -> Option<RichText> {
1012 match &self.extension {
1013 ClassExtension::Monograph(r) => r.abstract_text.clone(),
1014 ClassExtension::CollectionComponent(r) => r.abstract_text.clone(),
1015 ClassExtension::SerialComponent(r) => r.abstract_text.clone(),
1016 _ => None,
1017 }
1018 }
1019
1020 pub fn container_title(&self) -> Option<Title> {
1022 match &self.extension {
1023 ClassExtension::Monograph(r) => r.container.as_ref().and_then(|c| match c {
1024 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1025 WorkRelation::Id(_) => None,
1026 }),
1027 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
1028 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1029 WorkRelation::Id(_) => None,
1030 }),
1031 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
1032 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1033 WorkRelation::Id(_) => None,
1034 }),
1035 ClassExtension::Serial(r) => r.container.as_ref().and_then(|c| match c {
1036 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1037 WorkRelation::Id(_) => None,
1038 }),
1039 ClassExtension::LegalCase(r) => r.reporter.clone().map(Title::Single),
1040 ClassExtension::Statute(r) => r.code.clone().map(Title::Single),
1041 ClassExtension::Treaty(r) => r.reporter.clone().map(Title::Single),
1042 ClassExtension::Event(r) => r.container.as_ref().and_then(|c| match c {
1043 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1044 WorkRelation::Id(_) => None,
1045 }),
1046 ClassExtension::AudioVisual(r) => r.container.as_ref().and_then(|c| match c {
1047 WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
1048 WorkRelation::Id(_) => None,
1049 }),
1050 _ => None,
1051 }
1052 }
1053
1054 pub fn volume(&self) -> Option<NumOrStr> {
1056 match &self.extension {
1057 ClassExtension::Monograph(r) => r
1058 .volume
1059 .clone()
1060 .or_else(|| self.find_numbering(NumberingType::Volume))
1061 .map(NumOrStr::Str),
1062 ClassExtension::Collection(r) => r
1063 .volume
1064 .clone()
1065 .or_else(|| self.find_numbering(NumberingType::Volume))
1066 .map(NumOrStr::Str),
1067 ClassExtension::CollectionComponent(r) => r
1068 .volume
1069 .clone()
1070 .or_else(|| self.find_numbering(NumberingType::Volume))
1071 .map(NumOrStr::Str),
1072 ClassExtension::SerialComponent(r) => r
1073 .volume
1074 .clone()
1075 .or_else(|| self.find_numbering(NumberingType::Volume))
1076 .map(NumOrStr::Str),
1077 ClassExtension::Classic(r) => r
1078 .volume
1079 .clone()
1080 .or_else(|| self.find_numbering(NumberingType::Volume))
1081 .map(NumOrStr::Str),
1082 ClassExtension::LegalCase(r) => r.volume.clone().map(NumOrStr::Str),
1083 ClassExtension::Statute(r) => r.volume.clone().map(NumOrStr::Str),
1084 ClassExtension::Treaty(r) => r.volume.clone().map(NumOrStr::Str),
1085 ClassExtension::Regulation(r) => r.volume.clone().map(NumOrStr::Str),
1086 _ => self
1087 .find_numbering(NumberingType::Volume)
1088 .map(NumOrStr::Str),
1089 }
1090 }
1091
1092 pub fn collection_number(&self) -> Option<String> {
1094 match &self.extension {
1095 ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
1096 WorkRelation::Embedded(p) => p.collection_number(),
1097 WorkRelation::Id(_) => None,
1098 }),
1099 _ => self.find_numbering(NumberingType::Volume),
1100 }
1101 }
1102
1103 pub fn issue(&self) -> Option<NumOrStr> {
1105 match &self.extension {
1106 ClassExtension::Monograph(r) => r
1107 .issue
1108 .clone()
1109 .or_else(|| self.find_numbering(NumberingType::Issue))
1110 .map(NumOrStr::Str),
1111 ClassExtension::Collection(r) => r
1112 .issue
1113 .clone()
1114 .or_else(|| self.find_numbering(NumberingType::Issue))
1115 .map(NumOrStr::Str),
1116 ClassExtension::CollectionComponent(r) => r
1117 .issue
1118 .clone()
1119 .or_else(|| self.find_numbering(NumberingType::Issue))
1120 .map(NumOrStr::Str),
1121 ClassExtension::SerialComponent(r) => r
1122 .issue
1123 .clone()
1124 .or_else(|| self.find_numbering(NumberingType::Issue))
1125 .map(NumOrStr::Str),
1126 ClassExtension::Classic(r) => r
1127 .issue
1128 .clone()
1129 .or_else(|| self.find_numbering(NumberingType::Issue))
1130 .map(NumOrStr::Str),
1131 _ => self.find_numbering(NumberingType::Issue).map(NumOrStr::Str),
1132 }
1133 }
1134
1135 pub fn pages(&self) -> Option<NumOrStr> {
1137 match &self.extension {
1138 ClassExtension::CollectionComponent(r) => r.pages.clone(),
1139 ClassExtension::SerialComponent(r) => r.pages.clone().map(NumOrStr::Str),
1140 ClassExtension::LegalCase(r) => r.page.clone().map(NumOrStr::Str),
1141 ClassExtension::Statute(r) => r.page.clone().map(NumOrStr::Str),
1142 ClassExtension::Treaty(r) => r.page.clone().map(NumOrStr::Str),
1143 _ => None,
1144 }
1145 }
1146
1147 pub fn authority(&self) -> Option<String> {
1149 match &self.extension {
1150 ClassExtension::LegalCase(r) => r.authority.clone(),
1151 ClassExtension::Statute(r) => r.authority.clone(),
1152 ClassExtension::Hearing(r) => r.authority.clone(),
1153 ClassExtension::Regulation(r) => r.authority.clone(),
1154 ClassExtension::Brief(r) => r.authority.clone(),
1155 ClassExtension::Patent(r) => r.authority.clone(),
1156 ClassExtension::Standard(r) => r.authority.clone(),
1157 _ => None,
1158 }
1159 }
1160
1161 pub fn reporter(&self) -> Option<String> {
1163 match &self.extension {
1164 ClassExtension::LegalCase(r) => r.reporter.clone(),
1165 ClassExtension::Treaty(r) => r.reporter.clone(),
1166 _ => None,
1167 }
1168 }
1169
1170 pub fn code(&self) -> Option<String> {
1172 match &self.extension {
1173 ClassExtension::Statute(r) => r.code.clone(),
1174 ClassExtension::Regulation(r) => r.code.clone(),
1175 _ => None,
1176 }
1177 }
1178
1179 pub fn section(&self) -> Option<String> {
1181 match &self.extension {
1182 ClassExtension::Statute(r) => r.section.clone(),
1183 ClassExtension::Regulation(r) => r.section.clone(),
1184 ClassExtension::Classic(_) => self.find_numbering(NumberingType::Section),
1185 _ => None,
1186 }
1187 }
1188
1189 pub fn number(&self) -> Option<String> {
1191 match &self.extension {
1192 ClassExtension::Monograph(r) => r
1193 .number
1194 .clone()
1195 .or_else(|| self.find_numbering(NumberingType::Number)),
1196 ClassExtension::Statute(r) => r.number.clone(),
1197 ClassExtension::Collection(r) => r
1198 .number
1199 .clone()
1200 .or_else(|| self.find_numbering(NumberingType::Number)),
1201 ClassExtension::CollectionComponent(r) => r
1202 .number
1203 .clone()
1204 .or_else(|| self.find_numbering(NumberingType::Number)),
1205 ClassExtension::SerialComponent(r) => r
1206 .number
1207 .clone()
1208 .or_else(|| self.find_numbering(NumberingType::Number)),
1209 ClassExtension::Classic(r) => r
1210 .number
1211 .clone()
1212 .or_else(|| self.find_numbering(NumberingType::Number)),
1213 _ => self.find_numbering(NumberingType::Number),
1214 }
1215 }
1216
1217 pub fn report_number(&self) -> Option<String> {
1219 match &self.extension {
1220 ClassExtension::Monograph(_) => self.find_numbering(NumberingType::Report),
1221 _ => None,
1222 }
1223 }
1224
1225 pub fn edition(&self) -> Option<String> {
1227 match &self.extension {
1228 ClassExtension::Monograph(r) => r
1229 .edition
1230 .clone()
1231 .or_else(|| self.find_numbering(NumberingType::Edition)),
1232 ClassExtension::Collection(r) => r
1233 .edition
1234 .clone()
1235 .or_else(|| self.find_numbering(NumberingType::Edition)),
1236 ClassExtension::CollectionComponent(r) => r
1237 .edition
1238 .clone()
1239 .or_else(|| self.find_numbering(NumberingType::Edition)),
1240 ClassExtension::SerialComponent(r) => r
1241 .edition
1242 .clone()
1243 .or_else(|| self.find_numbering(NumberingType::Edition)),
1244 ClassExtension::Classic(r) => r
1245 .edition
1246 .clone()
1247 .or_else(|| self.find_numbering(NumberingType::Edition)),
1248 _ => self.find_numbering(NumberingType::Edition),
1249 }
1250 }
1251
1252 pub fn accessed(&self) -> Option<EdtfString> {
1254 class_dispatch!(&self.extension, |r| r.accessed.clone(), unknown(_) => None)
1255 }
1256
1257 fn original_embedded(&self) -> Option<&InputReference> {
1264 let relation = match &self.extension {
1265 ClassExtension::Monograph(r) => r.original.as_ref(),
1266 ClassExtension::CollectionComponent(r) => r.original.as_ref(),
1267 ClassExtension::SerialComponent(r) => r.original.as_ref(),
1268 ClassExtension::LegalCase(r) => r.original.as_ref(),
1269 ClassExtension::Statute(r) => r.original.as_ref(),
1270 ClassExtension::Treaty(r) => r.original.as_ref(),
1271 ClassExtension::Hearing(r) => r.original.as_ref(),
1272 ClassExtension::Regulation(r) => r.original.as_ref(),
1273 ClassExtension::Brief(r) => r.original.as_ref(),
1274 ClassExtension::Classic(r) => r.original.as_ref(),
1275 ClassExtension::Patent(r) => r.original.as_ref(),
1276 ClassExtension::Dataset(r) => r.original.as_ref(),
1277 ClassExtension::Standard(r) => r.original.as_ref(),
1278 ClassExtension::Software(r) => r.original.as_ref(),
1279 ClassExtension::Event(r) => r.original.as_ref(),
1280 ClassExtension::AudioVisual(r) => r.core.original.as_ref(),
1281 _ => None,
1282 }?;
1283 match relation {
1284 WorkRelation::Embedded(p) => Some(p),
1285 WorkRelation::Id(_) => None,
1286 }
1287 }
1288
1289 pub fn original_date(&self) -> Option<EdtfString> {
1291 self.original_embedded()?.csl_issued_date()
1292 }
1293
1294 pub fn original_title(&self) -> Option<Title> {
1296 self.original_embedded()?.title()
1297 }
1298
1299 pub fn original_publisher_str(&self) -> Option<String> {
1301 self.original_embedded()?
1302 .publisher_str()
1303 .filter(|value| !value.is_empty())
1304 }
1305
1306 pub fn original_publisher_place(&self) -> Option<String> {
1308 self.original_embedded()?.publisher_place()
1309 }
1310
1311 pub fn isbn(&self) -> Option<String> {
1313 match &self.extension {
1314 ClassExtension::Monograph(r) => r.isbn.clone(),
1315 _ => None,
1316 }
1317 }
1318
1319 pub fn issn(&self) -> Option<String> {
1321 match &self.extension {
1322 ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
1323 WorkRelation::Embedded(s) => s.issn(),
1324 WorkRelation::Id(_) => None,
1325 }),
1326 ClassExtension::Serial(r) => r.issn.clone(),
1327 _ => None,
1328 }
1329 }
1330
1331 pub fn keywords(&self) -> Option<Vec<String>> {
1333 match &self.extension {
1334 ClassExtension::Monograph(r) => r.keywords.clone(),
1335 ClassExtension::CollectionComponent(r) => r.keywords.clone(),
1336 ClassExtension::SerialComponent(r) => r.keywords.clone(),
1337 ClassExtension::Collection(r) => r.keywords.clone(),
1338 ClassExtension::Serial(_) => None,
1339 ClassExtension::LegalCase(r) => r.keywords.clone(),
1340 ClassExtension::Statute(r) => r.keywords.clone(),
1341 ClassExtension::Treaty(r) => r.keywords.clone(),
1342 ClassExtension::Hearing(r) => r.keywords.clone(),
1343 ClassExtension::Regulation(r) => r.keywords.clone(),
1344 ClassExtension::Brief(r) => r.keywords.clone(),
1345 ClassExtension::Classic(r) => r.keywords.clone(),
1346 ClassExtension::Patent(r) => r.keywords.clone(),
1347 ClassExtension::Dataset(r) => r.keywords.clone(),
1348 ClassExtension::Standard(r) => r.keywords.clone(),
1349 ClassExtension::Software(r) => r.keywords.clone(),
1350 ClassExtension::Event(_) => None,
1351 ClassExtension::AudioVisual(_) => None,
1352 ClassExtension::Unknown(_) => None,
1353 }
1354 }
1355
1356 pub fn language(&self) -> Option<LangID> {
1358 class_dispatch!(
1359 &self.extension,
1360 |r| r.language.clone(),
1361 audio_visual(r) => r.core.language.clone(),
1362 unknown(_) => None
1363 )
1364 }
1365
1366 pub fn field_languages(&self) -> &FieldLanguageMap {
1368 class_dispatch!(
1369 &self.extension,
1370 |r| &r.field_languages,
1371 unknown(_) => &EMPTY_FIELD_LANGUAGES
1372 )
1373 }
1374
1375 pub fn set_id(&mut self, id: impl Into<RefID>) {
1381 let id = id.into();
1382 class_dispatch!(&mut self.extension, |r| r.id = Some(id.clone()), unknown(data) => {
1383 data.fields
1384 .insert("id".to_string(), JsonValue::String(id.to_string()));
1385 });
1386 }
1387
1388 pub fn ref_type(&self) -> String {
1390 match &self.extension {
1391 ClassExtension::Monograph(r) => self.monograph_ref_type(r),
1392 ClassExtension::CollectionComponent(r) => collection_component_ref_type(r),
1393 ClassExtension::SerialComponent(r) => serial_component_ref_type(r),
1394 ClassExtension::Collection(r) => match r.r#type {
1395 CollectionType::EditedBook => "book",
1396 _ => "collection",
1397 }
1398 .to_string(),
1399 ClassExtension::Serial(r) => match r.r#type {
1400 SerialType::AcademicJournal => "article-journal",
1401 SerialType::Magazine => "article-magazine",
1402 SerialType::Newspaper => "article-newspaper",
1403 SerialType::BroadcastProgram => "broadcast",
1404 _ => "serial",
1405 }
1406 .to_string(),
1407 ClassExtension::LegalCase(_) => "legal-case".to_string(),
1408 ClassExtension::Statute(_) => "statute".to_string(),
1409 ClassExtension::Treaty(_) => "treaty".to_string(),
1410 ClassExtension::Hearing(_) => "hearing".to_string(),
1411 ClassExtension::Regulation(_) => "regulation".to_string(),
1412 ClassExtension::Brief(_) => "brief".to_string(),
1413 ClassExtension::Classic(_) => "classic".to_string(),
1414 ClassExtension::Patent(_) => "patent".to_string(),
1415 ClassExtension::Dataset(_) => "dataset".to_string(),
1416 ClassExtension::Standard(_) => "standard".to_string(),
1417 ClassExtension::Software(_) => "software".to_string(),
1418 ClassExtension::Event(r) => event_ref_type(r).to_string(),
1419 ClassExtension::AudioVisual(r) => audio_visual_ref_type(&r.r#type).to_string(),
1420 ClassExtension::Unknown(data) => {
1421 debug_assert!(
1431 !ReferenceClass::KNOWN.contains(&data.class.as_str()),
1432 "ClassExtension::Unknown should never wrap a known class string (got `{}`)",
1433 data.class
1434 );
1435 data.class.clone()
1436 }
1437 }
1438 }
1439
1440 fn monograph_ref_type(&self, r: &Monograph) -> String {
1441 match r.r#type {
1442 MonographType::Book => if r
1443 .medium
1444 .as_deref()
1445 .is_some_and(|m| m.to_ascii_lowercase().contains("interview"))
1446 {
1447 "interview"
1448 } else {
1449 "book"
1450 }
1451 .to_string(),
1452 MonographType::Manual => "manual".to_string(),
1453 MonographType::Report => "report".to_string(),
1454 MonographType::Thesis => "thesis".to_string(),
1455 MonographType::Webpage => "webpage".to_string(),
1456 MonographType::Post => "post".to_string(),
1457 MonographType::Interview => "interview".to_string(),
1458 MonographType::Manuscript => "manuscript".to_string(),
1459 MonographType::Preprint => "preprint".to_string(),
1460 MonographType::PersonalCommunication => "personal-communication".to_string(),
1461 MonographType::Document => {
1462 if let Some(genre) = r.genre.as_deref()
1463 && matches!(genre, "bill-proceeding" | "bill-record")
1464 {
1465 return genre.to_string();
1466 }
1467 if self.genre().as_deref() == Some("conference-paper") {
1468 return "paper-conference".to_string();
1469 }
1470 if r.medium
1471 .as_deref()
1472 .is_some_and(|m| m.to_ascii_lowercase().contains("interview"))
1473 {
1474 "interview"
1475 } else {
1476 "document"
1477 }
1478 .to_string()
1479 }
1480 _ => r.r#type.as_str().to_string(),
1481 }
1482 }
1483}
1484
1485fn collection_component_ref_type(r: &CollectionComponent) -> String {
1486 match r.r#type {
1487 MonographComponentType::Chapter => match r.genre.as_deref() {
1488 Some("entry-dictionary") => "entry-dictionary",
1489 Some("entry-encyclopedia") => "entry-encyclopedia",
1490 _ => "chapter",
1491 }
1492 .to_string(),
1493 MonographComponentType::Document => "paper-conference".to_string(),
1494 _ => r.r#type.as_str().to_string(),
1495 }
1496}
1497
1498fn serial_component_ref_type(r: &SerialComponent) -> String {
1499 if r.genre.as_deref() == Some("entry-encyclopedia") {
1500 return "entry-encyclopedia".to_string();
1501 }
1502 let container_type = r.container.as_ref().and_then(|c| match c {
1503 WorkRelation::Embedded(p) => Some(p.ref_type()),
1504 WorkRelation::Id(_) => None,
1505 });
1506 match container_type.as_deref() {
1507 Some("article-magazine") => "article-magazine".to_string(),
1508 Some("article-newspaper") => "article-newspaper".to_string(),
1509 Some("broadcast") => if r
1510 .genre
1511 .as_deref()
1512 .is_some_and(|g| g.to_ascii_lowercase().contains("film"))
1513 {
1514 "motion-picture"
1515 } else {
1516 "broadcast"
1517 }
1518 .to_string(),
1519 _ => "article-journal".to_string(),
1520 }
1521}
1522
1523fn event_ref_type(r: &Event) -> &'static str {
1524 let lowered = r.genre.as_deref().map(str::to_ascii_lowercase);
1525 match lowered.as_deref() {
1526 Some(g) if g.contains("conference") || g.contains("paper") => "paper-conference",
1527 Some(g) if g.contains("broadcast") => "broadcast",
1528 Some(g) if g.contains("talk") || g.contains("speech") => "speech",
1529 _ => "event",
1530 }
1531}
1532
1533fn audio_visual_ref_type(kind: &AudioVisualType) -> &'static str {
1534 match kind {
1535 AudioVisualType::Film => "motion-picture",
1536 AudioVisualType::Episode | AudioVisualType::Broadcast => "broadcast",
1537 AudioVisualType::Recording => "song",
1538 }
1539}
1540
1541fn collect_contributors_by_role(
1546 entries: &[ContributorEntry],
1547 role: &ContributorRole,
1548) -> Option<Contributor> {
1549 let mut matches = entries
1550 .iter()
1551 .filter(|e| &e.role == role)
1552 .map(|e| &e.contributor);
1553 let first = matches.next()?;
1554 let Some(second) = matches.next() else {
1555 return Some(first.clone());
1556 };
1557 let list = std::iter::once(first)
1558 .chain(std::iter::once(second))
1559 .chain(matches)
1560 .cloned()
1561 .collect();
1562 Some(Contributor::ContributorList(ContributorList(list)))
1563}