use crate::reference::types::{MultilingualString, Place};
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "bindings")]
use specta::Type;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "bindings", derive(Type))]
#[serde(rename_all = "kebab-case")]
pub enum ContributorGender {
Masculine,
Feminine,
Neuter,
Common,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "bindings", derive(Type))]
#[serde(untagged)]
pub enum Contributor {
SimpleName(SimpleName),
StructuredName(StructuredName),
Multilingual(MultilingualName),
ContributorList(ContributorList),
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "bindings", derive(Type))]
#[serde(rename_all = "kebab-case")]
pub struct MultilingualName {
pub original: StructuredName,
#[serde(skip_serializing_if = "Option::is_none")]
pub lang: Option<crate::reference::types::LangID>,
#[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
pub transliterations: std::collections::HashMap<String, StructuredName>,
#[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
pub translations: std::collections::HashMap<crate::reference::types::LangID, StructuredName>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "bindings", derive(Type))]
pub struct SimpleName {
pub name: MultilingualString,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<Place>,
#[serde(rename = "short-name", skip_serializing_if = "Option::is_none")]
pub short_name: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "bindings", derive(Type))]
#[serde(rename_all = "kebab-case")]
pub struct StructuredName {
pub given: MultilingualString,
pub family: MultilingualString,
#[serde(skip_serializing_if = "Option::is_none")]
pub suffix: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dropping_particle: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub non_dropping_particle: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "bindings", derive(Type))]
pub struct ContributorList(pub Vec<Contributor>);
impl Contributor {
pub fn to_names_vec(&self) -> Vec<FlatName> {
match self {
Contributor::SimpleName(n) => vec![FlatName {
literal: Some(n.name.to_string()),
short_name: n.short_name.clone(),
..Default::default()
}],
Contributor::StructuredName(n) => vec![FlatName {
given: Some(n.given.to_string()),
family: Some(n.family.to_string()),
suffix: n.suffix.clone(),
dropping_particle: n.dropping_particle.clone(),
non_dropping_particle: n.non_dropping_particle.clone(),
..Default::default()
}],
Contributor::Multilingual(m) => vec![FlatName {
given: Some(m.original.given.to_string()),
family: Some(m.original.family.to_string()),
suffix: m.original.suffix.clone(),
dropping_particle: m.original.dropping_particle.clone(),
non_dropping_particle: m.original.non_dropping_particle.clone(),
..Default::default()
}],
Contributor::ContributorList(l) => l.0.iter().flat_map(|c| c.to_names_vec()).collect(),
}
}
pub fn name(&self) -> Option<String> {
match self {
Contributor::SimpleName(n) => Some(n.name.to_string()),
Contributor::Multilingual(m) => {
Some(format!("{} {}", m.original.given, m.original.family))
}
_ => None,
}
}
pub fn location(&self) -> Option<String> {
match self {
Contributor::SimpleName(n) => n.location.clone().map(Into::into),
_ => None,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FlatName {
pub family: Option<String>,
pub given: Option<String>,
pub suffix: Option<String>,
pub dropping_particle: Option<String>,
pub non_dropping_particle: Option<String>,
pub literal: Option<String>,
pub short_name: Option<String>,
}
impl FlatName {
pub fn family_or_literal(&self) -> &str {
if let Some(ref f) = self.family {
f
} else if let Some(ref l) = self.literal {
l
} else {
""
}
}
}
impl fmt::Display for Contributor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Contributor::SimpleName(n) => write!(f, "{}", n.name),
Contributor::StructuredName(n) => write!(f, "{} {}", n.given, n.family),
Contributor::Multilingual(m) => write!(f, "{} {}", m.original.given, m.original.family),
Contributor::ContributorList(l) => write!(f, "{}", l),
}
}
}
impl fmt::Display for ContributorList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let names: Vec<String> = self.0.iter().map(|c| c.to_string()).collect();
write!(f, "{}", names.join(", "))
}
}
crate::tolerant_enum! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ContributorRole {
Author = "author",
Editor = "editor",
Translator = "translator",
Director = "director",
Performer = "performer",
Composer = "composer",
Illustrator = "illustrator",
Narrator = "narrator",
Host = "host",
Guest = "guest",
Interviewer = "interviewer",
Recipient = "recipient",
Compiler = "compiler",
Producer = "producer",
Writer = "writer"
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "bindings", derive(Type))]
#[serde(rename_all = "kebab-case")]
pub struct ContributorEntry {
pub role: ContributorRole,
pub contributor: Contributor,
#[serde(skip_serializing_if = "Option::is_none")]
pub gender: Option<ContributorGender>,
}