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