#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::embedded;
use crate::template::{LocalizedTemplateVariants, Template};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub enum TemplatePreset {
Apa,
ChicagoAuthorDate,
Vancouver,
Ieee,
Harvard,
NumericCitation,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum TemplateReference {
Preset(TemplatePreset),
Uri(String),
}
impl From<TemplatePreset> for TemplateReference {
fn from(preset: TemplatePreset) -> Self {
TemplateReference::Preset(preset)
}
}
impl TemplateReference {
#[must_use]
pub fn citation_template(&self) -> Option<Template> {
match self {
Self::Preset(preset) => Some(preset.citation_template()),
Self::Uri(_) => None,
}
}
#[must_use]
pub fn bibliography_template(&self) -> Option<Template> {
match self {
Self::Preset(preset) => Some(preset.bibliography_template()),
Self::Uri(_) => None,
}
}
}
impl TemplatePreset {
#[must_use]
pub fn citation_template(&self) -> Template {
match self {
TemplatePreset::Apa => embedded::apa_citation(),
TemplatePreset::ChicagoAuthorDate => embedded::chicago_author_date_citation(),
TemplatePreset::Vancouver => embedded::vancouver_citation(),
TemplatePreset::Ieee => embedded::ieee_citation(),
TemplatePreset::Harvard => embedded::harvard_citation(),
TemplatePreset::NumericCitation => embedded::numeric_citation(),
}
}
#[must_use]
pub fn bibliography_template(&self) -> Template {
match self {
TemplatePreset::Apa => embedded::apa_bibliography(),
TemplatePreset::ChicagoAuthorDate => embedded::chicago_author_date_bibliography(),
TemplatePreset::Vancouver => embedded::vancouver_bibliography(),
TemplatePreset::Ieee => embedded::ieee_bibliography(),
TemplatePreset::Harvard => embedded::harvard_bibliography(),
TemplatePreset::NumericCitation => embedded::vancouver_bibliography(),
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub struct LocalizedTemplateSpec {
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
pub template: Template,
#[serde(skip_serializing_if = "Option::is_none")]
pub type_variants: Option<LocalizedTemplateVariants>,
#[serde(
flatten,
default,
skip_serializing_if = "std::collections::BTreeMap::is_empty"
)]
#[cfg_attr(feature = "schema", schemars(skip))]
pub unknown_fields: std::collections::BTreeMap<String, serde_yaml::Value>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ResolvedLocalizedTemplate {
pub template: Template,
pub locale: Option<String>,
pub type_variants: Option<LocalizedTemplateVariants>,
}
pub(crate) fn matched_localized_template(
locales: &[LocalizedTemplateSpec],
language: &str,
) -> Option<ResolvedLocalizedTemplate> {
let exact = locales.iter().find_map(|spec| {
spec.locale.as_ref()?.iter().find_map(|candidate| {
candidate
.eq_ignore_ascii_case(language)
.then(|| ResolvedLocalizedTemplate {
template: spec.template.clone(),
locale: Some(candidate.clone()),
type_variants: spec.type_variants.clone(),
})
})
});
if exact.is_some() {
return exact;
}
let primary = language.split(['-', '_']).next().unwrap_or(language);
locales.iter().find_map(|spec| {
spec.locale.as_ref()?.iter().find_map(|candidate| {
candidate
.split(['-', '_'])
.next()
.is_some_and(|candidate_primary| candidate_primary.eq_ignore_ascii_case(primary))
.then(|| ResolvedLocalizedTemplate {
template: spec.template.clone(),
locale: Some(candidate.clone()),
type_variants: spec.type_variants.clone(),
})
})
})
}