use crate::grouping::{GroupSort, GroupSortKey, SortKey as GroupSortKey_};
use crate::options::multilingual::{MultilingualConfig, MultilingualMode};
use crate::options::{
AndOptions, ContributorConfig, DateConfig, DelimiterPrecedesLast, DemoteNonDroppingParticle,
DisplayAsSort, MonthFormat, NameForm, ShortenListOptions, Sort, SortKey, SortSpec, Substitute,
SubstituteKey, TitleRendering, TitlesConfig,
};
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum ContributorPreset {
Apa,
Chicago,
Vancouver,
Ieee,
Harvard,
Springer,
NumericCompact,
NumericMedium,
NumericTight,
NumericLarge,
NumericAllAuthors,
NumericGivenDot,
AnnualReviews,
MathPhys,
SocSciFirst,
PhysicsNumeric,
}
impl ContributorPreset {
fn config_named_presets(&self) -> ContributorConfig {
match self {
ContributorPreset::Apa => ContributorConfig {
display_as_sort: Some(DisplayAsSort::First),
and: Some(AndOptions::Symbol),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some(". ".to_string()),
name_form: Some(NameForm::Initials),
shorten: Some(ShortenListOptions {
min: 21,
use_first: 19,
..Default::default()
}),
..Default::default()
},
ContributorPreset::Chicago => ContributorConfig {
display_as_sort: Some(DisplayAsSort::First),
and: Some(AndOptions::Text),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Contextual),
..Default::default()
},
ContributorPreset::Vancouver => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
initialize_with: Some("".to_string()),
name_form: Some(NameForm::Initials),
shorten: Some(ShortenListOptions {
min: 7,
use_first: 6,
..Default::default()
}),
..Default::default()
},
ContributorPreset::Ieee => ContributorConfig {
display_as_sort: Some(DisplayAsSort::None),
and: Some(AndOptions::Text),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some(". ".to_string()),
name_form: Some(NameForm::Initials),
..Default::default()
},
ContributorPreset::Harvard => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::Text),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some(".".to_string()),
name_form: Some(NameForm::Initials),
..Default::default()
},
ContributorPreset::Springer => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some("".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(" ".to_string()),
shorten: Some(ShortenListOptions {
min: 5,
use_first: 3,
..Default::default()
}),
..Default::default()
},
#[allow(clippy::unreachable, reason = "Subset of variants handled here")]
_ => unreachable!(),
}
}
fn config_numeric_presets(&self) -> ContributorConfig {
match self {
ContributorPreset::NumericCompact => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some("".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(" ".to_string()),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::SortOnly),
shorten: Some(ShortenListOptions {
min: 7,
use_first: 6,
..Default::default()
}),
..Default::default()
},
ContributorPreset::NumericMedium => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some("".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(" ".to_string()),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::SortOnly),
shorten: Some(ShortenListOptions {
min: 4,
use_first: 3,
..Default::default()
}),
..Default::default()
},
ContributorPreset::NumericTight => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some("".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(" ".to_string()),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::SortOnly),
shorten: Some(ShortenListOptions {
min: 7,
use_first: 3,
..Default::default()
}),
..Default::default()
},
ContributorPreset::NumericLarge => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some("".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(" ".to_string()),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::SortOnly),
shorten: Some(ShortenListOptions {
min: 11,
use_first: 10,
..Default::default()
}),
..Default::default()
},
ContributorPreset::NumericAllAuthors => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some("".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(" ".to_string()),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::Never),
..Default::default()
},
ContributorPreset::NumericGivenDot => ContributorConfig {
display_as_sort: Some(DisplayAsSort::None),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some(".".to_string()),
name_form: Some(NameForm::Initials),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::SortOnly),
..Default::default()
},
#[allow(clippy::unreachable, reason = "Subset of variants handled here")]
_ => unreachable!(),
}
}
fn config_specialty_presets(&self) -> ContributorConfig {
match self {
ContributorPreset::AnnualReviews => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Never),
initialize_with: Some("".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(" ".to_string()),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::Never),
shorten: Some(ShortenListOptions {
min: 7,
use_first: 5,
..Default::default()
}),
..Default::default()
},
ContributorPreset::MathPhys => ContributorConfig {
display_as_sort: Some(DisplayAsSort::All),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some(".".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(", ".to_string()),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::SortOnly),
..Default::default()
},
ContributorPreset::SocSciFirst => ContributorConfig {
display_as_sort: Some(DisplayAsSort::First),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
delimiter_precedes_last: Some(DelimiterPrecedesLast::Always),
initialize_with: Some(". ".to_string()),
name_form: Some(NameForm::Initials),
sort_separator: Some(", ".to_string()),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::SortOnly),
..Default::default()
},
ContributorPreset::PhysicsNumeric => ContributorConfig {
display_as_sort: Some(DisplayAsSort::None),
and: Some(AndOptions::None),
delimiter: Some(", ".to_string()),
initialize_with: Some(". ".to_string()),
name_form: Some(NameForm::Initials),
demote_non_dropping_particle: Some(DemoteNonDroppingParticle::SortOnly),
..Default::default()
},
#[allow(clippy::unreachable, reason = "Subset of variants handled here")]
_ => unreachable!(),
}
}
pub fn config(&self) -> ContributorConfig {
match self {
ContributorPreset::Apa
| ContributorPreset::Chicago
| ContributorPreset::Vancouver
| ContributorPreset::Ieee
| ContributorPreset::Harvard
| ContributorPreset::Springer => self.config_named_presets(),
ContributorPreset::NumericCompact
| ContributorPreset::NumericMedium
| ContributorPreset::NumericTight
| ContributorPreset::NumericLarge
| ContributorPreset::NumericAllAuthors
| ContributorPreset::NumericGivenDot => self.config_numeric_presets(),
ContributorPreset::AnnualReviews
| ContributorPreset::MathPhys
| ContributorPreset::SocSciFirst
| ContributorPreset::PhysicsNumeric => self.config_specialty_presets(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum DatePreset {
Long,
Short,
Numeric,
Iso,
}
impl DatePreset {
pub fn config(&self) -> DateConfig {
match self {
DatePreset::Long => DateConfig {
month: MonthFormat::Long,
..Default::default()
},
DatePreset::Short => DateConfig {
month: MonthFormat::Short,
..Default::default()
},
DatePreset::Numeric => DateConfig {
month: MonthFormat::Numeric,
..Default::default()
},
DatePreset::Iso => DateConfig {
month: MonthFormat::Numeric,
uncertainty_marker: None,
approximation_marker: None,
..Default::default()
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum TitlePreset {
Apa,
Chicago,
Ieee,
Humanities,
JournalEmphasis,
Scientific,
}
impl TitlePreset {
pub fn config(&self) -> TitlesConfig {
use crate::options::titles::TextCase;
let emph_rendering = TitleRendering {
emph: Some(true),
..Default::default()
};
match self {
TitlePreset::Apa => TitlesConfig {
component: Some(TitleRendering {
text_case: Some(TextCase::SentenceApa),
..Default::default()
}),
monograph: Some(TitleRendering {
text_case: Some(TextCase::SentenceApa),
emph: Some(true),
..Default::default()
}),
periodical: Some(emph_rendering),
..Default::default()
},
TitlePreset::Chicago | TitlePreset::Ieee => TitlesConfig {
component: Some(TitleRendering {
quote: Some(true),
..Default::default()
}),
monograph: Some(emph_rendering.clone()),
periodical: Some(emph_rendering),
..Default::default()
},
TitlePreset::Humanities => TitlesConfig {
component: Some(TitleRendering::default()),
monograph: Some(emph_rendering.clone()),
periodical: Some(emph_rendering.clone()),
serial: Some(emph_rendering),
..Default::default()
},
TitlePreset::JournalEmphasis => TitlesConfig {
component: Some(TitleRendering::default()),
periodical: Some(emph_rendering.clone()),
serial: Some(emph_rendering),
..Default::default()
},
TitlePreset::Scientific => TitlesConfig {
component: Some(TitleRendering {
text_case: Some(TextCase::SentenceNlm),
..Default::default()
}),
monograph: Some(TitleRendering {
text_case: Some(TextCase::SentenceNlm),
..Default::default()
}),
periodical: Some(TitleRendering::default()),
..Default::default()
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum SortPreset {
AuthorDateTitle,
AuthorTitleDate,
CitationNumber,
}
impl SortPreset {
pub fn sort(&self) -> Sort {
match self {
SortPreset::AuthorDateTitle => Sort {
shorten_names: false,
render_substitutions: false,
template: vec![
SortSpec {
key: SortKey::Author,
ascending: true,
},
SortSpec {
key: SortKey::Year,
ascending: true,
},
SortSpec {
key: SortKey::Title,
ascending: true,
},
],
},
SortPreset::AuthorTitleDate => Sort {
shorten_names: false,
render_substitutions: false,
template: vec![
SortSpec {
key: SortKey::Author,
ascending: true,
},
SortSpec {
key: SortKey::Title,
ascending: true,
},
SortSpec {
key: SortKey::Year,
ascending: true,
},
],
},
SortPreset::CitationNumber => Sort {
shorten_names: false,
render_substitutions: false,
template: vec![SortSpec {
key: SortKey::CitationNumber,
ascending: true,
}],
},
}
}
pub fn group_sort(&self) -> GroupSort {
let keys: Vec<GroupSortKey> = match self {
SortPreset::AuthorDateTitle => vec![
GroupSortKey {
key: GroupSortKey_::Author,
ascending: true,
order: None,
sort_order: None,
},
GroupSortKey {
key: GroupSortKey_::Issued,
ascending: true,
order: None,
sort_order: None,
},
GroupSortKey {
key: GroupSortKey_::Title,
ascending: true,
order: None,
sort_order: None,
},
],
SortPreset::AuthorTitleDate => vec![
GroupSortKey {
key: GroupSortKey_::Author,
ascending: true,
order: None,
sort_order: None,
},
GroupSortKey {
key: GroupSortKey_::Title,
ascending: true,
order: None,
sort_order: None,
},
GroupSortKey {
key: GroupSortKey_::Issued,
ascending: true,
order: None,
sort_order: None,
},
],
SortPreset::CitationNumber => vec![],
};
GroupSort { template: keys }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum SubstitutePreset {
Standard,
EditorFirst,
TitleFirst,
EditorShort,
EditorLong,
EditorTranslatorShort,
EditorTranslatorLong,
EditorTitleShort,
EditorTitleLong,
EditorTranslatorTitleShort,
EditorTranslatorTitleLong,
}
impl SubstitutePreset {
pub fn config(&self) -> Substitute {
match self {
SubstitutePreset::Standard => Substitute {
contributor_role_form: None,
template: vec![
SubstituteKey::Editor,
SubstituteKey::Title,
SubstituteKey::Translator,
],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorFirst => Substitute {
contributor_role_form: None,
template: vec![
SubstituteKey::Editor,
SubstituteKey::Translator,
SubstituteKey::Title,
],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::TitleFirst => Substitute {
contributor_role_form: None,
template: vec![
SubstituteKey::Title,
SubstituteKey::Editor,
SubstituteKey::Translator,
],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorShort => Substitute {
contributor_role_form: Some("short".to_string()),
template: vec![SubstituteKey::Editor],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorLong => Substitute {
contributor_role_form: Some("long".to_string()),
template: vec![SubstituteKey::Editor],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorTranslatorShort => Substitute {
contributor_role_form: Some("short".to_string()),
template: vec![SubstituteKey::Editor, SubstituteKey::Translator],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorTranslatorLong => Substitute {
contributor_role_form: Some("long".to_string()),
template: vec![SubstituteKey::Editor, SubstituteKey::Translator],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorTitleShort => Substitute {
contributor_role_form: Some("short".to_string()),
template: vec![SubstituteKey::Editor, SubstituteKey::Title],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorTitleLong => Substitute {
contributor_role_form: Some("long".to_string()),
template: vec![SubstituteKey::Editor, SubstituteKey::Title],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorTranslatorTitleShort => Substitute {
contributor_role_form: Some("short".to_string()),
template: vec![
SubstituteKey::Editor,
SubstituteKey::Translator,
SubstituteKey::Title,
],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
SubstitutePreset::EditorTranslatorTitleLong => Substitute {
contributor_role_form: Some("long".to_string()),
template: vec![
SubstituteKey::Editor,
SubstituteKey::Translator,
SubstituteKey::Title,
],
overrides: HashMap::new(),
role_substitute: HashMap::new(),
unknown_fields: std::collections::BTreeMap::new(),
},
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum MultilingualPreset {
RomanizedTranslated,
RomanizedOnly,
}
impl MultilingualPreset {
pub fn config(self) -> MultilingualConfig {
match self {
MultilingualPreset::RomanizedTranslated => MultilingualConfig {
title_mode: Some(MultilingualMode::Combined),
name_mode: Some(MultilingualMode::Transliterated),
preferred_script: Some("Latn".to_string()),
..Default::default()
},
MultilingualPreset::RomanizedOnly => MultilingualConfig {
title_mode: Some(MultilingualMode::Transliterated),
name_mode: Some(MultilingualMode::Transliterated),
preferred_script: Some("Latn".to_string()),
..Default::default()
},
}
}
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum MultilingualConfigEntry {
Preset(MultilingualPreset),
Explicit(Box<MultilingualConfig>),
}
impl MultilingualConfigEntry {
pub fn resolve(self) -> MultilingualConfig {
match self {
MultilingualConfigEntry::Preset(p) => p.config(),
MultilingualConfigEntry::Explicit(c) => *c,
}
}
}
impl<'de> serde::Deserialize<'de> for MultilingualConfigEntry {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct EntryVisitor;
impl<'de> serde::de::Visitor<'de> for EntryVisitor {
type Value = MultilingualConfigEntry;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "a multilingual preset name or an explicit config block")
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
let preset =
MultilingualPreset::deserialize(serde::de::value::StrDeserializer::new(v))?;
Ok(MultilingualConfigEntry::Preset(preset))
}
fn visit_map<A: serde::de::MapAccess<'de>>(
self,
map: A,
) -> Result<Self::Value, A::Error> {
let config = MultilingualConfig::deserialize(
serde::de::value::MapAccessDeserializer::new(map),
)?;
Ok(MultilingualConfigEntry::Explicit(Box::new(config)))
}
}
deserializer.deserialize_any(EntryVisitor)
}
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing,
clippy::todo,
clippy::unimplemented,
clippy::unreachable,
clippy::get_unwrap,
reason = "Panicking is acceptable and often desired in tests."
)]
mod tests {
use super::*;
#[test]
fn test_contributor_preset_apa() {
let config = ContributorPreset::Apa.config();
assert_eq!(config.and, Some(AndOptions::Symbol));
assert_eq!(config.display_as_sort, Some(DisplayAsSort::First));
let shorten = config.shorten.unwrap();
assert_eq!(shorten.min, 21);
assert_eq!(shorten.use_first, 19);
}
#[test]
fn test_contributor_preset_chicago() {
let config = ContributorPreset::Chicago.config();
assert_eq!(config.and, Some(AndOptions::Text));
assert_eq!(config.display_as_sort, Some(DisplayAsSort::First));
}
#[test]
fn test_contributor_preset_vancouver() {
let config = ContributorPreset::Vancouver.config();
assert_eq!(config.and, Some(AndOptions::None));
assert_eq!(config.display_as_sort, Some(DisplayAsSort::All));
}
#[test]
fn test_contributor_preset_springer() {
let config = ContributorPreset::Springer.config();
assert_eq!(config.and, Some(AndOptions::None));
assert_eq!(config.display_as_sort, Some(DisplayAsSort::All));
assert_eq!(config.sort_separator, Some(" ".to_string()));
let shorten = config.shorten.unwrap();
assert_eq!(shorten.min, 5);
assert_eq!(shorten.use_first, 3);
}
#[test]
fn test_contributor_preset_numeric_compact() {
let config = ContributorPreset::NumericCompact.config();
assert_eq!(config.and, Some(AndOptions::None));
assert_eq!(config.display_as_sort, Some(DisplayAsSort::All));
assert_eq!(config.sort_separator, Some(" ".to_string()));
assert_eq!(
config.demote_non_dropping_particle,
Some(DemoteNonDroppingParticle::SortOnly)
);
let shorten = config.shorten.unwrap();
assert_eq!(shorten.min, 7);
assert_eq!(shorten.use_first, 6);
}
#[test]
fn test_contributor_preset_numeric_all_authors() {
let config = ContributorPreset::NumericAllAuthors.config();
assert_eq!(config.and, Some(AndOptions::None));
assert_eq!(config.display_as_sort, Some(DisplayAsSort::All));
assert_eq!(config.sort_separator, Some(" ".to_string()));
assert_eq!(config.initialize_with, Some("".to_string()));
assert_eq!(
config.demote_non_dropping_particle,
Some(DemoteNonDroppingParticle::Never)
);
assert!(config.shorten.is_none());
}
#[test]
fn test_contributor_preset_numeric_given_dot() {
let config = ContributorPreset::NumericGivenDot.config();
assert_eq!(config.and, Some(AndOptions::None));
assert_eq!(config.display_as_sort, Some(DisplayAsSort::None));
assert_eq!(config.initialize_with, Some(".".to_string()));
assert_eq!(
config.demote_non_dropping_particle,
Some(DemoteNonDroppingParticle::SortOnly)
);
assert_eq!(
config.delimiter_precedes_last,
Some(DelimiterPrecedesLast::Always)
);
}
#[test]
fn test_date_preset_long() {
let config = DatePreset::Long.config();
assert_eq!(config.month, MonthFormat::Long);
assert!(config.uncertainty_marker.is_some());
}
#[test]
fn test_date_preset_iso() {
let config = DatePreset::Iso.config();
assert_eq!(config.month, MonthFormat::Numeric);
assert!(config.uncertainty_marker.is_none());
assert!(config.approximation_marker.is_none());
}
#[test]
fn test_title_preset_apa() {
let config = TitlePreset::Apa.config();
let component = config.component.unwrap();
assert!(component.quote.is_none() || component.quote == Some(false));
let monograph = config.monograph.unwrap();
assert_eq!(monograph.emph, Some(true));
}
#[test]
fn test_title_preset_chicago() {
let config = TitlePreset::Chicago.config();
let component = config.component.unwrap();
assert_eq!(component.quote, Some(true));
let monograph = config.monograph.unwrap();
assert_eq!(monograph.emph, Some(true));
}
#[test]
fn test_preset_yaml_roundtrip() {
let yaml = r#"apa"#;
let preset: ContributorPreset = serde_yaml::from_str(yaml).unwrap();
assert_eq!(preset, ContributorPreset::Apa);
let serialized = serde_yaml::to_string(&preset).unwrap();
assert!(serialized.contains("apa"));
}
#[test]
fn test_all_presets_serialize() {
let contributor_presets = vec![
ContributorPreset::Apa,
ContributorPreset::Chicago,
ContributorPreset::Vancouver,
ContributorPreset::Ieee,
ContributorPreset::Harvard,
ContributorPreset::Springer,
ContributorPreset::NumericCompact,
ContributorPreset::NumericMedium,
ContributorPreset::NumericTight,
ContributorPreset::NumericLarge,
ContributorPreset::NumericAllAuthors,
ContributorPreset::NumericGivenDot,
ContributorPreset::AnnualReviews,
ContributorPreset::MathPhys,
ContributorPreset::SocSciFirst,
ContributorPreset::PhysicsNumeric,
];
for preset in contributor_presets {
let yaml = serde_yaml::to_string(&preset).unwrap();
let _: ContributorPreset = serde_yaml::from_str(&yaml).unwrap();
}
let date_presets = vec![
DatePreset::Long,
DatePreset::Short,
DatePreset::Numeric,
DatePreset::Iso,
];
for preset in date_presets {
let yaml = serde_yaml::to_string(&preset).unwrap();
let _: DatePreset = serde_yaml::from_str(&yaml).unwrap();
}
let title_presets = vec![
TitlePreset::Apa,
TitlePreset::Chicago,
TitlePreset::Ieee,
TitlePreset::Humanities,
TitlePreset::JournalEmphasis,
TitlePreset::Scientific,
];
for preset in title_presets {
let yaml = serde_yaml::to_string(&preset).unwrap();
let _: TitlePreset = serde_yaml::from_str(&yaml).unwrap();
}
let substitute_presets = vec![
SubstitutePreset::Standard,
SubstitutePreset::EditorFirst,
SubstitutePreset::TitleFirst,
SubstitutePreset::EditorShort,
SubstitutePreset::EditorLong,
SubstitutePreset::EditorTranslatorShort,
SubstitutePreset::EditorTranslatorLong,
SubstitutePreset::EditorTitleShort,
SubstitutePreset::EditorTitleLong,
SubstitutePreset::EditorTranslatorTitleShort,
SubstitutePreset::EditorTranslatorTitleLong,
];
for preset in substitute_presets {
let yaml = serde_yaml::to_string(&preset).unwrap();
let _: SubstitutePreset = serde_yaml::from_str(&yaml).unwrap();
}
}
#[test]
fn test_substitute_preset_standard() {
let config = SubstitutePreset::Standard.config();
assert_eq!(
config.template,
vec![
SubstituteKey::Editor,
SubstituteKey::Title,
SubstituteKey::Translator,
]
);
}
#[test]
fn test_substitute_preset_title_first() {
let config = SubstitutePreset::TitleFirst.config();
assert_eq!(config.template[0], SubstituteKey::Title);
}
}