use serde_json::Value as JsonValue;
use url::Url;
use super::classes::class_dispatch;
use super::contributor::{Contributor, ContributorEntry, ContributorList, ContributorRole};
use super::date::EdtfString;
use super::types::common::{
FieldLanguageMap, HasNumbering, LangID, MultilingualString, NumOrStr, NumberingType, Publisher,
RefID, RichText, Title,
};
use super::types::legal::{Brief, Hearing, LegalCase, Regulation, Statute, Treaty};
use super::types::specialized::{
AudioVisualType, AudioVisualWork, Classic, Dataset, Event, Patent, Software, Standard,
};
use super::types::structural::{
Collection, CollectionComponent, CollectionType, Monograph, MonographComponentType,
MonographType, Serial, SerialComponent, SerialType,
};
use super::{
ClassExtension, EMPTY_FIELD_LANGUAGES, InputReference, ReferenceClass, UnknownClassData,
WorkRelation,
};
impl InputReference {
#[must_use]
pub fn class(&self) -> ReferenceClass {
self.extension.reference_class()
}
#[must_use]
pub fn extension(&self) -> &ClassExtension {
&self.extension
}
#[must_use]
pub fn extension_mut(&mut self) -> &mut ClassExtension {
&mut self.extension
}
#[must_use]
pub fn as_monograph(&self) -> Option<&Monograph> {
match &self.extension {
ClassExtension::Monograph(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_collection_component(&self) -> Option<&CollectionComponent> {
match &self.extension {
ClassExtension::CollectionComponent(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_serial_component(&self) -> Option<&SerialComponent> {
match &self.extension {
ClassExtension::SerialComponent(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_collection(&self) -> Option<&Collection> {
match &self.extension {
ClassExtension::Collection(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_serial(&self) -> Option<&Serial> {
match &self.extension {
ClassExtension::Serial(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_legal_case(&self) -> Option<&LegalCase> {
match &self.extension {
ClassExtension::LegalCase(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_statute(&self) -> Option<&Statute> {
match &self.extension {
ClassExtension::Statute(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_treaty(&self) -> Option<&Treaty> {
match &self.extension {
ClassExtension::Treaty(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_hearing(&self) -> Option<&Hearing> {
match &self.extension {
ClassExtension::Hearing(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_regulation(&self) -> Option<&Regulation> {
match &self.extension {
ClassExtension::Regulation(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_brief(&self) -> Option<&Brief> {
match &self.extension {
ClassExtension::Brief(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_classic(&self) -> Option<&Classic> {
match &self.extension {
ClassExtension::Classic(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_patent(&self) -> Option<&Patent> {
match &self.extension {
ClassExtension::Patent(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_dataset(&self) -> Option<&Dataset> {
match &self.extension {
ClassExtension::Dataset(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_standard(&self) -> Option<&Standard> {
match &self.extension {
ClassExtension::Standard(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_software(&self) -> Option<&Software> {
match &self.extension {
ClassExtension::Software(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_event(&self) -> Option<&Event> {
match &self.extension {
ClassExtension::Event(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_audio_visual(&self) -> Option<&AudioVisualWork> {
match &self.extension {
ClassExtension::AudioVisual(reference) => Some(reference.as_ref()),
_ => None,
}
}
#[must_use]
pub fn unknown_class(&self) -> Option<&UnknownClassData> {
match &self.extension {
ClassExtension::Unknown(data) => Some(data),
_ => None,
}
}
fn numbered(&self) -> Option<&dyn HasNumbering> {
match &self.extension {
ClassExtension::Monograph(reference) => Some(reference.as_ref()),
ClassExtension::Collection(reference) => Some(reference.as_ref()),
ClassExtension::CollectionComponent(reference) => Some(reference.as_ref()),
ClassExtension::SerialComponent(reference) => Some(reference.as_ref()),
ClassExtension::Classic(reference) => Some(reference.as_ref()),
ClassExtension::AudioVisual(reference) => Some(reference.as_ref()),
_ => None,
}
}
fn find_numbering(&self, numbering_type: NumberingType) -> Option<String> {
self.numbered()
.and_then(|reference| reference.find_numbering(numbering_type))
}
pub fn numbering_value(&self, numbering_type: &NumberingType) -> Option<String> {
self.find_numbering(numbering_type.clone())
}
pub fn id(&self) -> Option<RefID> {
class_dispatch!(&self.extension, |r| r.id.clone(), unknown(data) => data
.fields
.get("id")
.and_then(JsonValue::as_str)
.map(|id| RefID(id.to_string())))
}
pub fn author(&self) -> Option<Contributor> {
use crate::reference::contributor::ContributorRole as DataRole;
match &self.extension {
ClassExtension::Monograph(r) => {
collect_contributors_by_role(&r.contributors, &DataRole::Author)
.or_else(|| r.author.clone())
}
ClassExtension::CollectionComponent(r) => {
collect_contributors_by_role(&r.contributors, &DataRole::Author)
.or_else(|| r.author.clone())
}
ClassExtension::SerialComponent(r) => {
let explicit_author =
collect_contributors_by_role(&r.contributors, &DataRole::Author)
.or_else(|| r.author.clone());
explicit_author.or_else(|| {
let av_like = r
.medium
.as_deref()
.map(|value| {
let lowered = value.to_ascii_lowercase();
lowered.contains("podcast")
|| lowered.contains("tv")
|| lowered.contains("film")
|| lowered.contains("video")
})
.unwrap_or(false)
|| r.genre
.as_deref()
.map(|value| {
let lowered = value.to_ascii_lowercase();
lowered.contains("broadcast") || lowered.contains("film")
})
.unwrap_or(false);
if av_like {
collect_contributors_by_role(&r.contributors, &DataRole::Producer).or_else(
|| collect_contributors_by_role(&r.contributors, &DataRole::Host),
)
} else {
None
}
})
}
ClassExtension::Treaty(r) => r.author.clone(),
ClassExtension::Brief(r) => r.author.clone(),
ClassExtension::Classic(r) => r.author.clone(),
ClassExtension::Patent(r) => r.author.clone(),
ClassExtension::Dataset(r) => r.author.clone(),
ClassExtension::Software(r) => r.author.clone(),
ClassExtension::Event(r) => {
collect_contributors_by_role(&r.contributors, &DataRole::Performer)
.or_else(|| {
collect_contributors_by_role(
&r.contributors,
&DataRole::Unknown("organizer".to_string()),
)
})
.or_else(|| collect_contributors_by_role(&r.contributors, &DataRole::Author))
}
ClassExtension::AudioVisual(r) => {
let explicit_author =
collect_contributors_by_role(&r.core.contributors, &DataRole::Author);
match r.r#type {
AudioVisualType::Film | AudioVisualType::Episode => {
explicit_author.or_else(|| {
collect_contributors_by_role(&r.core.contributors, &DataRole::Director)
})
}
AudioVisualType::Recording => explicit_author.or_else(|| {
collect_contributors_by_role(&r.core.contributors, &DataRole::Composer)
.or_else(|| {
collect_contributors_by_role(
&r.core.contributors,
&DataRole::Performer,
)
})
}),
AudioVisualType::Broadcast => explicit_author
.or_else(|| {
collect_contributors_by_role(&r.core.contributors, &DataRole::Director)
})
.or_else(|| {
collect_contributors_by_role(&r.core.contributors, &DataRole::Producer)
}),
}
}
_ => None,
}
}
pub fn editor(&self) -> Option<Contributor> {
match &self.extension {
ClassExtension::Monograph(r) => {
collect_contributors_by_role(&r.contributors, &ContributorRole::Editor)
.or_else(|| r.editor.clone())
}
ClassExtension::Collection(r) => {
collect_contributors_by_role(&r.contributors, &ContributorRole::Editor)
.or_else(|| r.editor.clone())
}
ClassExtension::CollectionComponent(r) => r
.container
.as_ref()
.and_then(|c| match c {
WorkRelation::Embedded(p) => p.editor(),
WorkRelation::Id(_) => None,
})
.or_else(|| {
collect_contributors_by_role(&r.contributors, &ContributorRole::Editor)
}),
ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.editor(),
WorkRelation::Id(_) => None,
}),
ClassExtension::Serial(r) => {
collect_contributors_by_role(&r.contributors, &ContributorRole::Editor)
.or_else(|| r.editor.clone())
}
ClassExtension::Classic(r) => r.editor.clone(),
ClassExtension::AudioVisual(_) => None,
_ => None,
}
}
pub fn translator(&self) -> Option<Contributor> {
match &self.extension {
ClassExtension::Monograph(r) => {
collect_contributors_by_role(&r.contributors, &ContributorRole::Translator)
.or_else(|| r.translator.clone())
}
ClassExtension::CollectionComponent(r) => {
collect_contributors_by_role(&r.contributors, &ContributorRole::Translator)
.or_else(|| r.translator.clone())
}
ClassExtension::SerialComponent(r) => {
collect_contributors_by_role(&r.contributors, &ContributorRole::Translator)
.or_else(|| r.translator.clone())
}
ClassExtension::Collection(r) => {
collect_contributors_by_role(&r.contributors, &ContributorRole::Translator)
.or_else(|| r.translator.clone())
}
ClassExtension::Classic(r) => r.translator.clone(),
ClassExtension::AudioVisual(r) => {
collect_contributors_by_role(&r.core.contributors, &ContributorRole::Translator)
}
_ => None,
}
}
pub fn publisher(&self) -> Option<Publisher> {
match &self.extension {
ClassExtension::Monograph(r) => r.publisher.clone(),
ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher(),
WorkRelation::Id(_) => None,
}),
ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher(),
WorkRelation::Id(_) => None,
}),
ClassExtension::Collection(r) => r.publisher.clone(),
ClassExtension::Serial(r) => r.publisher.clone(),
ClassExtension::Classic(r) => r.publisher.clone(),
ClassExtension::Dataset(r) => r.publisher.clone(),
ClassExtension::Standard(r) => r.publisher.clone(),
ClassExtension::Software(r) => r.publisher.clone(),
ClassExtension::AudioVisual(r) => r.publisher.clone(),
_ => None,
}
}
pub fn contributor(&self, role: ContributorRole) -> Option<Contributor> {
let entries = self.all_contributor_entries();
collect_contributors_by_role(entries, &role)
}
pub fn contributor_entries(&self, role: &ContributorRole) -> Vec<&ContributorEntry> {
self.all_contributor_entries()
.iter()
.filter(|entry| &entry.role == role)
.collect()
}
pub fn all_contributor_entries(&self) -> &[ContributorEntry] {
match &self.extension {
ClassExtension::Monograph(r) => &r.contributors,
ClassExtension::Collection(r) => &r.contributors,
ClassExtension::CollectionComponent(r) => &r.contributors,
ClassExtension::Serial(r) => &r.contributors,
ClassExtension::SerialComponent(r) => &r.contributors,
ClassExtension::Event(r) => &r.contributors,
ClassExtension::AudioVisual(r) => &r.core.contributors,
_ => &[],
}
}
pub fn title(&self) -> Option<Title> {
match &self.extension {
ClassExtension::Monograph(r) => match (&r.title, &r.short_title) {
(Some(Title::Single(long)), Some(short)) => {
Some(Title::Shorthand(short.clone(), long.clone()))
}
_ => r.title.clone(),
},
ClassExtension::CollectionComponent(r) => r.title.clone(),
ClassExtension::SerialComponent(r) => r.title.clone(),
ClassExtension::Collection(r) => match (&r.title, &r.short_title) {
(Some(Title::Single(long)), Some(short)) => {
Some(Title::Shorthand(short.clone(), long.clone()))
}
_ => r.title.clone(),
},
ClassExtension::Serial(r) => match (&r.title, &r.short_title) {
(Some(Title::Single(long)), Some(short)) => {
Some(Title::Shorthand(short.clone(), long.clone()))
}
_ => r.title.clone(),
},
ClassExtension::LegalCase(r) => r.title.clone(),
ClassExtension::Statute(r) => r.title.clone(),
ClassExtension::Treaty(r) => r.title.clone(),
ClassExtension::Hearing(r) => r.title.clone(),
ClassExtension::Regulation(r) => r.title.clone(),
ClassExtension::Brief(r) => r.title.clone(),
ClassExtension::Classic(r) => r.title.clone(),
ClassExtension::Patent(r) => r.title.clone(),
ClassExtension::Dataset(r) => r.title.clone(),
ClassExtension::Standard(r) => r.title.clone(),
ClassExtension::Software(r) => r.title.clone(),
ClassExtension::Event(r) => r.title.clone(),
ClassExtension::AudioVisual(r) => match (&r.core.title, &r.core.short_title) {
(Some(Title::Single(long)), Some(short)) => {
Some(Title::Shorthand(short.clone(), long.clone()))
}
_ => r.core.title.clone(),
},
ClassExtension::Unknown(data) => data
.fields
.get("title")
.and_then(JsonValue::as_str)
.map(|title| Title::Single(title.to_string())),
}
}
fn non_empty_date(date: EdtfString) -> Option<EdtfString> {
if date.is_empty() { None } else { Some(date) }
}
pub fn created(&self) -> Option<EdtfString> {
match &self.extension {
ClassExtension::Monograph(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::CollectionComponent(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::SerialComponent(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Collection(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Serial(_) => None,
ClassExtension::LegalCase(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Statute(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Treaty(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Hearing(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Regulation(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Brief(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Classic(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Patent(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Dataset(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Standard(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Software(r) => Self::non_empty_date(r.created.clone()),
ClassExtension::Event(_) => None,
ClassExtension::AudioVisual(r) => Self::non_empty_date(r.core.created.clone()),
ClassExtension::Unknown(_) => None,
}
}
pub fn issued(&self) -> Option<EdtfString> {
match &self.extension {
ClassExtension::Monograph(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::CollectionComponent(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::SerialComponent(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Collection(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Serial(_) => None,
ClassExtension::LegalCase(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Statute(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Treaty(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Hearing(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Regulation(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Brief(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Classic(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Patent(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Dataset(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Standard(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Software(r) => Self::non_empty_date(r.issued.clone()),
ClassExtension::Event(r) => r.date.clone(),
ClassExtension::AudioVisual(r) => Self::non_empty_date(r.core.issued.clone()),
ClassExtension::Unknown(_) => None,
}
}
pub fn csl_issued_date(&self) -> Option<EdtfString> {
self.issued().or_else(|| self.created())
}
pub fn doi(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.doi.clone(),
ClassExtension::CollectionComponent(r) => r.doi.clone(),
ClassExtension::SerialComponent(r) => r.doi.clone(),
ClassExtension::LegalCase(r) => r.doi.clone(),
ClassExtension::Dataset(r) => r.doi.clone(),
ClassExtension::Software(r) => r.doi.clone(),
ClassExtension::AudioVisual(_) => None,
_ => None,
}
}
pub fn ads_bibcode(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.ads_bibcode.clone(),
ClassExtension::SerialComponent(r) => r.ads_bibcode.clone(),
ClassExtension::AudioVisual(_) => None,
_ => None,
}
}
pub fn note(&self) -> Option<RichText> {
match &self.extension {
ClassExtension::Monograph(r) => r.note.clone(),
ClassExtension::CollectionComponent(r) => r.note.clone(),
ClassExtension::SerialComponent(r) => r.note.clone(),
ClassExtension::Collection(r) => r.note.clone(),
ClassExtension::Serial(r) => r.note.clone(),
ClassExtension::LegalCase(r) => r.note.clone(),
ClassExtension::Statute(r) => r.note.clone(),
ClassExtension::Treaty(r) => r.note.clone(),
ClassExtension::Standard(r) => r.note.clone(),
ClassExtension::Event(r) => r.note.clone(),
ClassExtension::AudioVisual(r) => r.note.clone(),
_ => None,
}
}
pub fn url(&self) -> Option<Url> {
match &self.extension {
ClassExtension::Monograph(r) => r.url.clone(),
ClassExtension::CollectionComponent(r) => r.url.clone(),
ClassExtension::SerialComponent(r) => r.url.clone(),
ClassExtension::Collection(r) => r.url.clone(),
ClassExtension::Serial(r) => r.url.clone(),
ClassExtension::LegalCase(r) => r.url.clone(),
ClassExtension::Statute(r) => r.url.clone(),
ClassExtension::Treaty(r) => r.url.clone(),
ClassExtension::Hearing(r) => r.url.clone(),
ClassExtension::Regulation(r) => r.url.clone(),
ClassExtension::Brief(r) => r.url.clone(),
ClassExtension::Classic(r) => r.url.clone(),
ClassExtension::Patent(r) => r.url.clone(),
ClassExtension::Dataset(r) => r.url.clone(),
ClassExtension::Standard(r) => r.url.clone(),
ClassExtension::Software(r) => r.url.clone(),
ClassExtension::Event(r) => r.url.clone(),
ClassExtension::AudioVisual(r) => r.url.clone(),
ClassExtension::Unknown(_) => None,
}
}
pub fn publisher_place(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r
.publisher
.as_ref()
.and_then(|p| p.place.clone().map(Into::into))
.or_else(|| {
r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher_place(),
_ => None,
})
}),
ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher_place(),
_ => None,
}),
ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher_place(),
_ => None,
}),
ClassExtension::Collection(r) => r
.publisher
.as_ref()
.and_then(|p| p.place.clone().map(Into::into))
.or_else(|| {
r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher_place(),
_ => None,
})
}),
ClassExtension::Serial(_) => None,
ClassExtension::Classic(r) => r
.publisher
.as_ref()
.and_then(|p| p.place.clone().map(Into::into)),
ClassExtension::Dataset(r) => r
.publisher
.as_ref()
.and_then(|p| p.place.clone().map(Into::into)),
ClassExtension::Standard(r) => r
.publisher
.as_ref()
.and_then(|p| p.place.clone().map(Into::into)),
ClassExtension::Software(r) => r
.publisher
.as_ref()
.and_then(|p| p.place.clone().map(Into::into)),
ClassExtension::Event(r) => r.location.clone(),
ClassExtension::AudioVisual(r) => r
.publisher
.as_ref()
.and_then(|p| p.place.clone().map(Into::into)),
_ => None,
}
}
pub fn publisher_str(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r
.publisher
.as_ref()
.map(|p| p.name.to_string())
.or_else(|| {
r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher_str(),
_ => None,
})
}),
ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher_str(),
_ => None,
}),
ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher_str(),
_ => None,
}),
ClassExtension::Collection(r) => r
.publisher
.as_ref()
.map(|p| p.name.to_string())
.or_else(|| {
r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.publisher_str(),
_ => None,
})
}),
ClassExtension::Serial(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
ClassExtension::Classic(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
ClassExtension::Dataset(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
ClassExtension::Standard(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
ClassExtension::Software(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
ClassExtension::Event(r) => r.network.clone(),
ClassExtension::AudioVisual(r) => r.publisher.as_ref().map(|p| p.name.to_string()),
_ => None,
}
}
pub(super) fn normalize_genre_medium(s: &str) -> String {
let lower = s.to_ascii_lowercase();
lower
.split(|c: char| c.is_whitespace() || c == '_')
.filter(|p| !p.is_empty())
.collect::<Vec<_>>()
.join("-")
}
pub fn genre(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => {
r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
}
ClassExtension::CollectionComponent(r) => {
r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
}
ClassExtension::SerialComponent(r) => {
r.genre.as_ref().map(|g| Self::normalize_genre_medium(g))
}
ClassExtension::Event(r) => r.genre.as_ref().map(|g| Self::normalize_genre_medium(g)),
ClassExtension::AudioVisual(r) => r
.core
.genre
.as_ref()
.map(|g| Self::normalize_genre_medium(g)),
_ => None,
}
}
pub fn archive(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.archive.clone(),
_ => None,
}
}
pub fn archive_location(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r
.archive_info
.as_ref()
.and_then(|info| info.location.clone())
.or_else(|| r.archive_location.clone()),
ClassExtension::CollectionComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.location.clone())
}
ClassExtension::SerialComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.location.clone())
}
_ => None,
}
}
pub fn archive_name(&self) -> Option<MultilingualString> {
match &self.extension {
ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.name.clone()),
ClassExtension::CollectionComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.name.clone())
}
ClassExtension::SerialComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.name.clone())
}
_ => None,
}
}
pub fn archive_place(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r
.archive_info
.as_ref()
.and_then(|i| i.place.clone().map(Into::into)),
ClassExtension::CollectionComponent(r) => r
.archive_info
.as_ref()
.and_then(|i| i.place.clone().map(Into::into)),
ClassExtension::SerialComponent(r) => r
.archive_info
.as_ref()
.and_then(|i| i.place.clone().map(Into::into)),
_ => None,
}
}
pub fn archive_collection(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => {
r.archive_info.as_ref().and_then(|i| i.collection.clone())
}
ClassExtension::CollectionComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.collection.clone())
}
ClassExtension::SerialComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.collection.clone())
}
_ => None,
}
}
pub fn archive_collection_id(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r
.archive_info
.as_ref()
.and_then(|i| i.collection_id.clone()),
ClassExtension::CollectionComponent(r) => r
.archive_info
.as_ref()
.and_then(|i| i.collection_id.clone()),
ClassExtension::SerialComponent(r) => r
.archive_info
.as_ref()
.and_then(|i| i.collection_id.clone()),
_ => None,
}
}
pub fn archive_series(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.series.clone()),
ClassExtension::CollectionComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.series.clone())
}
ClassExtension::SerialComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.series.clone())
}
_ => None,
}
}
pub fn archive_box(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.r#box.clone()),
ClassExtension::CollectionComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.r#box.clone())
}
ClassExtension::SerialComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.r#box.clone())
}
_ => None,
}
}
pub fn archive_folder(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.folder.clone()),
ClassExtension::CollectionComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.folder.clone())
}
ClassExtension::SerialComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.folder.clone())
}
_ => None,
}
}
pub fn archive_item(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.item.clone()),
ClassExtension::CollectionComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.item.clone())
}
ClassExtension::SerialComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.item.clone())
}
_ => None,
}
}
pub fn archive_url(&self) -> Option<Url> {
match &self.extension {
ClassExtension::Monograph(r) => r.archive_info.as_ref().and_then(|i| i.url.clone()),
ClassExtension::CollectionComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.url.clone())
}
ClassExtension::SerialComponent(r) => {
r.archive_info.as_ref().and_then(|i| i.url.clone())
}
_ => None,
}
}
pub fn status(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.status.clone(),
ClassExtension::CollectionComponent(r) => r.status.clone(),
ClassExtension::SerialComponent(r) => r.status.clone(),
ClassExtension::Standard(r) => r.status.clone(),
_ => None,
}
}
pub fn eprint_id(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.eprint.as_ref().map(|e| e.id.clone()),
ClassExtension::CollectionComponent(r) => r.eprint.as_ref().map(|e| e.id.clone()),
ClassExtension::SerialComponent(r) => r.eprint.as_ref().map(|e| e.id.clone()),
_ => None,
}
}
pub fn eprint_server(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.eprint.as_ref().map(|e| e.server.clone()),
ClassExtension::CollectionComponent(r) => r.eprint.as_ref().map(|e| e.server.clone()),
ClassExtension::SerialComponent(r) => r.eprint.as_ref().map(|e| e.server.clone()),
_ => None,
}
}
pub fn eprint_class(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.eprint.as_ref().and_then(|e| e.class.clone()),
ClassExtension::CollectionComponent(r) => {
r.eprint.as_ref().and_then(|e| e.class.clone())
}
ClassExtension::SerialComponent(r) => r.eprint.as_ref().and_then(|e| e.class.clone()),
_ => None,
}
}
pub fn medium(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => {
r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
}
ClassExtension::CollectionComponent(r) => {
r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
}
ClassExtension::SerialComponent(r) => {
r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
}
ClassExtension::AudioVisual(r) => {
r.medium.as_ref().map(|m| Self::normalize_genre_medium(m))
}
_ => None,
}
}
pub fn version(&self) -> Option<String> {
match &self.extension {
ClassExtension::Dataset(r) => r.version.clone(),
ClassExtension::Software(r) => r.version.clone(),
_ => None,
}
}
pub fn abstract_text(&self) -> Option<RichText> {
match &self.extension {
ClassExtension::Monograph(r) => r.abstract_text.clone(),
ClassExtension::CollectionComponent(r) => r.abstract_text.clone(),
ClassExtension::SerialComponent(r) => r.abstract_text.clone(),
_ => None,
}
}
pub fn container_title(&self) -> Option<Title> {
match &self.extension {
ClassExtension::Monograph(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
WorkRelation::Id(_) => None,
}),
ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
WorkRelation::Id(_) => None,
}),
ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
WorkRelation::Id(_) => None,
}),
ClassExtension::Serial(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
WorkRelation::Id(_) => None,
}),
ClassExtension::LegalCase(r) => r.reporter.clone().map(Title::Single),
ClassExtension::Statute(r) => r.code.clone().map(Title::Single),
ClassExtension::Treaty(r) => r.reporter.clone().map(Title::Single),
ClassExtension::Event(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
WorkRelation::Id(_) => None,
}),
ClassExtension::AudioVisual(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.title().or_else(|| p.container_title()),
WorkRelation::Id(_) => None,
}),
_ => None,
}
}
pub fn volume(&self) -> Option<NumOrStr> {
match &self.extension {
ClassExtension::Monograph(r) => r
.volume
.clone()
.or_else(|| self.find_numbering(NumberingType::Volume))
.map(NumOrStr::Str),
ClassExtension::Collection(r) => r
.volume
.clone()
.or_else(|| self.find_numbering(NumberingType::Volume))
.map(NumOrStr::Str),
ClassExtension::CollectionComponent(r) => r
.volume
.clone()
.or_else(|| self.find_numbering(NumberingType::Volume))
.map(NumOrStr::Str),
ClassExtension::SerialComponent(r) => r
.volume
.clone()
.or_else(|| self.find_numbering(NumberingType::Volume))
.map(NumOrStr::Str),
ClassExtension::Classic(r) => r
.volume
.clone()
.or_else(|| self.find_numbering(NumberingType::Volume))
.map(NumOrStr::Str),
ClassExtension::LegalCase(r) => r.volume.clone().map(NumOrStr::Str),
ClassExtension::Statute(r) => r.volume.clone().map(NumOrStr::Str),
ClassExtension::Treaty(r) => r.volume.clone().map(NumOrStr::Str),
ClassExtension::Regulation(r) => r.volume.clone().map(NumOrStr::Str),
_ => self
.find_numbering(NumberingType::Volume)
.map(NumOrStr::Str),
}
}
pub fn collection_number(&self) -> Option<String> {
match &self.extension {
ClassExtension::CollectionComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => p.collection_number(),
WorkRelation::Id(_) => None,
}),
_ => self.find_numbering(NumberingType::Volume),
}
}
pub fn issue(&self) -> Option<NumOrStr> {
match &self.extension {
ClassExtension::Monograph(r) => r
.issue
.clone()
.or_else(|| self.find_numbering(NumberingType::Issue))
.map(NumOrStr::Str),
ClassExtension::Collection(r) => r
.issue
.clone()
.or_else(|| self.find_numbering(NumberingType::Issue))
.map(NumOrStr::Str),
ClassExtension::CollectionComponent(r) => r
.issue
.clone()
.or_else(|| self.find_numbering(NumberingType::Issue))
.map(NumOrStr::Str),
ClassExtension::SerialComponent(r) => r
.issue
.clone()
.or_else(|| self.find_numbering(NumberingType::Issue))
.map(NumOrStr::Str),
ClassExtension::Classic(r) => r
.issue
.clone()
.or_else(|| self.find_numbering(NumberingType::Issue))
.map(NumOrStr::Str),
_ => self.find_numbering(NumberingType::Issue).map(NumOrStr::Str),
}
}
pub fn pages(&self) -> Option<NumOrStr> {
match &self.extension {
ClassExtension::CollectionComponent(r) => r.pages.clone(),
ClassExtension::SerialComponent(r) => r.pages.clone().map(NumOrStr::Str),
ClassExtension::LegalCase(r) => r.page.clone().map(NumOrStr::Str),
ClassExtension::Statute(r) => r.page.clone().map(NumOrStr::Str),
ClassExtension::Treaty(r) => r.page.clone().map(NumOrStr::Str),
_ => None,
}
}
pub fn authority(&self) -> Option<String> {
match &self.extension {
ClassExtension::LegalCase(r) => r.authority.clone(),
ClassExtension::Statute(r) => r.authority.clone(),
ClassExtension::Hearing(r) => r.authority.clone(),
ClassExtension::Regulation(r) => r.authority.clone(),
ClassExtension::Brief(r) => r.authority.clone(),
ClassExtension::Patent(r) => r.authority.clone(),
ClassExtension::Standard(r) => r.authority.clone(),
_ => None,
}
}
pub fn reporter(&self) -> Option<String> {
match &self.extension {
ClassExtension::LegalCase(r) => r.reporter.clone(),
ClassExtension::Treaty(r) => r.reporter.clone(),
_ => None,
}
}
pub fn code(&self) -> Option<String> {
match &self.extension {
ClassExtension::Statute(r) => r.code.clone(),
ClassExtension::Regulation(r) => r.code.clone(),
_ => None,
}
}
pub fn section(&self) -> Option<String> {
match &self.extension {
ClassExtension::Statute(r) => r.section.clone(),
ClassExtension::Regulation(r) => r.section.clone(),
ClassExtension::Classic(_) => self.find_numbering(NumberingType::Section),
_ => None,
}
}
pub fn number(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r
.number
.clone()
.or_else(|| self.find_numbering(NumberingType::Number)),
ClassExtension::Statute(r) => r.number.clone(),
ClassExtension::Collection(r) => r
.number
.clone()
.or_else(|| self.find_numbering(NumberingType::Number)),
ClassExtension::CollectionComponent(r) => r
.number
.clone()
.or_else(|| self.find_numbering(NumberingType::Number)),
ClassExtension::SerialComponent(r) => r
.number
.clone()
.or_else(|| self.find_numbering(NumberingType::Number)),
ClassExtension::Classic(r) => r
.number
.clone()
.or_else(|| self.find_numbering(NumberingType::Number)),
_ => self.find_numbering(NumberingType::Number),
}
}
pub fn report_number(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(_) => self.find_numbering(NumberingType::Report),
_ => None,
}
}
pub fn edition(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r
.edition
.clone()
.or_else(|| self.find_numbering(NumberingType::Edition)),
ClassExtension::Collection(r) => r
.edition
.clone()
.or_else(|| self.find_numbering(NumberingType::Edition)),
ClassExtension::CollectionComponent(r) => r
.edition
.clone()
.or_else(|| self.find_numbering(NumberingType::Edition)),
ClassExtension::SerialComponent(r) => r
.edition
.clone()
.or_else(|| self.find_numbering(NumberingType::Edition)),
ClassExtension::Classic(r) => r
.edition
.clone()
.or_else(|| self.find_numbering(NumberingType::Edition)),
_ => self.find_numbering(NumberingType::Edition),
}
}
pub fn accessed(&self) -> Option<EdtfString> {
class_dispatch!(&self.extension, |r| r.accessed.clone(), unknown(_) => None)
}
fn original_embedded(&self) -> Option<&InputReference> {
let relation = match &self.extension {
ClassExtension::Monograph(r) => r.original.as_ref(),
ClassExtension::CollectionComponent(r) => r.original.as_ref(),
ClassExtension::SerialComponent(r) => r.original.as_ref(),
ClassExtension::LegalCase(r) => r.original.as_ref(),
ClassExtension::Statute(r) => r.original.as_ref(),
ClassExtension::Treaty(r) => r.original.as_ref(),
ClassExtension::Hearing(r) => r.original.as_ref(),
ClassExtension::Regulation(r) => r.original.as_ref(),
ClassExtension::Brief(r) => r.original.as_ref(),
ClassExtension::Classic(r) => r.original.as_ref(),
ClassExtension::Patent(r) => r.original.as_ref(),
ClassExtension::Dataset(r) => r.original.as_ref(),
ClassExtension::Standard(r) => r.original.as_ref(),
ClassExtension::Software(r) => r.original.as_ref(),
ClassExtension::Event(r) => r.original.as_ref(),
ClassExtension::AudioVisual(r) => r.core.original.as_ref(),
_ => None,
}?;
match relation {
WorkRelation::Embedded(p) => Some(p),
WorkRelation::Id(_) => None,
}
}
pub fn original_date(&self) -> Option<EdtfString> {
self.original_embedded()?.csl_issued_date()
}
pub fn original_title(&self) -> Option<Title> {
self.original_embedded()?.title()
}
pub fn original_publisher_str(&self) -> Option<String> {
self.original_embedded()?
.publisher_str()
.filter(|value| !value.is_empty())
}
pub fn original_publisher_place(&self) -> Option<String> {
self.original_embedded()?.publisher_place()
}
pub fn isbn(&self) -> Option<String> {
match &self.extension {
ClassExtension::Monograph(r) => r.isbn.clone(),
_ => None,
}
}
pub fn issn(&self) -> Option<String> {
match &self.extension {
ClassExtension::SerialComponent(r) => r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(s) => s.issn(),
WorkRelation::Id(_) => None,
}),
ClassExtension::Serial(r) => r.issn.clone(),
_ => None,
}
}
pub fn keywords(&self) -> Option<Vec<String>> {
match &self.extension {
ClassExtension::Monograph(r) => r.keywords.clone(),
ClassExtension::CollectionComponent(r) => r.keywords.clone(),
ClassExtension::SerialComponent(r) => r.keywords.clone(),
ClassExtension::Collection(r) => r.keywords.clone(),
ClassExtension::Serial(_) => None,
ClassExtension::LegalCase(r) => r.keywords.clone(),
ClassExtension::Statute(r) => r.keywords.clone(),
ClassExtension::Treaty(r) => r.keywords.clone(),
ClassExtension::Hearing(r) => r.keywords.clone(),
ClassExtension::Regulation(r) => r.keywords.clone(),
ClassExtension::Brief(r) => r.keywords.clone(),
ClassExtension::Classic(r) => r.keywords.clone(),
ClassExtension::Patent(r) => r.keywords.clone(),
ClassExtension::Dataset(r) => r.keywords.clone(),
ClassExtension::Standard(r) => r.keywords.clone(),
ClassExtension::Software(r) => r.keywords.clone(),
ClassExtension::Event(_) => None,
ClassExtension::AudioVisual(_) => None,
ClassExtension::Unknown(_) => None,
}
}
pub fn language(&self) -> Option<LangID> {
class_dispatch!(
&self.extension,
|r| r.language.clone(),
audio_visual(r) => r.core.language.clone(),
unknown(_) => None
)
}
pub fn field_languages(&self) -> &FieldLanguageMap {
class_dispatch!(
&self.extension,
|r| &r.field_languages,
unknown(_) => &EMPTY_FIELD_LANGUAGES
)
}
pub fn set_id(&mut self, id: impl Into<RefID>) {
let id = id.into();
class_dispatch!(&mut self.extension, |r| r.id = Some(id.clone()), unknown(data) => {
data.fields
.insert("id".to_string(), JsonValue::String(id.to_string()));
});
}
pub fn ref_type(&self) -> String {
match &self.extension {
ClassExtension::Monograph(r) => self.monograph_ref_type(r),
ClassExtension::CollectionComponent(r) => collection_component_ref_type(r),
ClassExtension::SerialComponent(r) => serial_component_ref_type(r),
ClassExtension::Collection(r) => match r.r#type {
CollectionType::EditedBook => "book",
_ => "collection",
}
.to_string(),
ClassExtension::Serial(r) => match r.r#type {
SerialType::AcademicJournal => "article-journal",
SerialType::Magazine => "article-magazine",
SerialType::Newspaper => "article-newspaper",
SerialType::BroadcastProgram => "broadcast",
_ => "serial",
}
.to_string(),
ClassExtension::LegalCase(_) => "legal-case".to_string(),
ClassExtension::Statute(_) => "statute".to_string(),
ClassExtension::Treaty(_) => "treaty".to_string(),
ClassExtension::Hearing(_) => "hearing".to_string(),
ClassExtension::Regulation(_) => "regulation".to_string(),
ClassExtension::Brief(_) => "brief".to_string(),
ClassExtension::Classic(_) => "classic".to_string(),
ClassExtension::Patent(_) => "patent".to_string(),
ClassExtension::Dataset(_) => "dataset".to_string(),
ClassExtension::Standard(_) => "standard".to_string(),
ClassExtension::Software(_) => "software".to_string(),
ClassExtension::Event(r) => event_ref_type(r).to_string(),
ClassExtension::AudioVisual(r) => audio_visual_ref_type(&r.r#type).to_string(),
ClassExtension::Unknown(data) => {
debug_assert!(
!ReferenceClass::KNOWN.contains(&data.class.as_str()),
"ClassExtension::Unknown should never wrap a known class string (got `{}`)",
data.class
);
data.class.clone()
}
}
}
fn monograph_ref_type(&self, r: &Monograph) -> String {
match r.r#type {
MonographType::Book => if r
.medium
.as_deref()
.is_some_and(|m| m.to_ascii_lowercase().contains("interview"))
{
"interview"
} else {
"book"
}
.to_string(),
MonographType::Manual => "manual".to_string(),
MonographType::Report => "report".to_string(),
MonographType::Thesis => "thesis".to_string(),
MonographType::Webpage => "webpage".to_string(),
MonographType::Post => "post".to_string(),
MonographType::Interview => "interview".to_string(),
MonographType::Manuscript => "manuscript".to_string(),
MonographType::Preprint => "preprint".to_string(),
MonographType::PersonalCommunication => "personal-communication".to_string(),
MonographType::Document => {
if let Some(genre) = r.genre.as_deref()
&& matches!(genre, "bill-proceeding" | "bill-record")
{
return genre.to_string();
}
if self.genre().as_deref() == Some("conference-paper") {
return "paper-conference".to_string();
}
if r.medium
.as_deref()
.is_some_and(|m| m.to_ascii_lowercase().contains("interview"))
{
"interview"
} else {
"document"
}
.to_string()
}
_ => r.r#type.as_str().to_string(),
}
}
}
fn collection_component_ref_type(r: &CollectionComponent) -> String {
match r.r#type {
MonographComponentType::Chapter => match r.genre.as_deref() {
Some("entry-dictionary") => "entry-dictionary",
Some("entry-encyclopedia") => "entry-encyclopedia",
_ => "chapter",
}
.to_string(),
MonographComponentType::Document => "paper-conference".to_string(),
_ => r.r#type.as_str().to_string(),
}
}
fn serial_component_ref_type(r: &SerialComponent) -> String {
if r.genre.as_deref() == Some("entry-encyclopedia") {
return "entry-encyclopedia".to_string();
}
let container_type = r.container.as_ref().and_then(|c| match c {
WorkRelation::Embedded(p) => Some(p.ref_type()),
WorkRelation::Id(_) => None,
});
match container_type.as_deref() {
Some("article-magazine") => "article-magazine".to_string(),
Some("article-newspaper") => "article-newspaper".to_string(),
Some("broadcast") => if r
.genre
.as_deref()
.is_some_and(|g| g.to_ascii_lowercase().contains("film"))
{
"motion-picture"
} else {
"broadcast"
}
.to_string(),
_ => "article-journal".to_string(),
}
}
fn event_ref_type(r: &Event) -> &'static str {
let lowered = r.genre.as_deref().map(str::to_ascii_lowercase);
match lowered.as_deref() {
Some(g) if g.contains("conference") || g.contains("paper") => "paper-conference",
Some(g) if g.contains("broadcast") => "broadcast",
Some(g) if g.contains("talk") || g.contains("speech") => "speech",
_ => "event",
}
}
fn audio_visual_ref_type(kind: &AudioVisualType) -> &'static str {
match kind {
AudioVisualType::Film => "motion-picture",
AudioVisualType::Episode | AudioVisualType::Broadcast => "broadcast",
AudioVisualType::Recording => "song",
}
}
fn collect_contributors_by_role(
entries: &[ContributorEntry],
role: &ContributorRole,
) -> Option<Contributor> {
let mut matches = entries
.iter()
.filter(|e| &e.role == role)
.map(|e| &e.contributor);
let first = matches.next()?;
let Some(second) = matches.next() else {
return Some(first.clone());
};
let list = std::iter::once(first)
.chain(std::iter::once(second))
.chain(matches)
.cloned()
.collect();
Some(Contributor::ContributorList(ContributorList(list)))
}