From 38a56c8dea69531befea140aa51ca913fe9ca586 Mon Sep 17 00:00:00 2001 From: PgBiel <9021226+PgBiel@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:50:08 -0300 Subject: [PATCH] Fix handling of trailing whitespaces in initials' delimiters (#257) * keep whitespace at the end of initial delimiters ...when not adding hyphens * add tests for person name retrieval order * add missing test with empty delimiter --- src/types/persons.rs | 91 ++++++++++++++++++++++++++++++++++++----- tests/citeproc-pass.txt | 2 + 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/src/types/persons.rs b/src/types/persons.rs index 4c05529b..87345076 100644 --- a/src/types/persons.rs +++ b/src/types/persons.rs @@ -234,8 +234,14 @@ impl Person { /// Formats the given name into initials. /// /// For example, `"Judith Beatrice"` would yield `"J. B."` if the - /// `delimiter` argument is set to `Some(".")`, `"Klaus-Peter"` would become - /// `"K-P"` without a delimiter. + /// `delimiter` argument is set to `Some(". ")`, `"Klaus-Peter"` would + /// become `"K-P"` without a delimiter (or with an empty delimiter). + /// + /// Whitespace at the end of the delimiter is kept when parts are separated + /// by whitespace (as in the first example), but not when parts of a + /// compound given name are separated by hyphens (for example, + /// `"Klaus-Peter"` produces `"K.-P."` instead of `"K. -P."` for a + /// delimiter of `Some(". ")`). pub fn initials( &self, buf: &mut impl std::fmt::Write, @@ -253,15 +259,21 @@ impl Person { if let Some(c) = gr.chars().next() { if c.is_whitespace() || c == '-' { if !collect { + let hyphenate = with_hyphen && c == '-'; if let Some(delimiter) = delimiter { - buf.write_str(delimiter.trim_end())?; + // Use the given delimiter, including any spaces at + // its end if there was a whitespace, but not if we + // should add a hyphen in a compound given name. + buf.write_str(if hyphenate { + delimiter.trim_end() + } else { + delimiter + })?; } collect = true; - if with_hyphen && c == '-' { - buf.write_char(c)?; - } else if delimiter.is_some() { - buf.write_char(' ')?; + if hyphenate { + buf.write_char('-')?; } } continue; @@ -277,7 +289,7 @@ impl Person { if non_empty && !collect { if let Some(delim) = delimiter { - buf.write_str(delim)?; + buf.write_str(delim.trim_end())?; } } @@ -315,7 +327,7 @@ impl Person { Ok(()) } - /// Get the name with the family name fist, the initials + /// Get the name with the family name first, the initials /// afterwards, separated by a comma. pub fn name_first(&self, initials: bool, prefix_given_name: bool) -> String { let mut res = if !prefix_given_name { @@ -331,7 +343,7 @@ impl Person { if initials { if self.given_name.is_some() { res += ", "; - self.initials(&mut res, Some("."), true).unwrap(); + self.initials(&mut res, Some(". "), true).unwrap(); } } else if let Some(given_name) = &self.given_name { res += ", "; @@ -361,7 +373,7 @@ impl Person { if initials { if self.given_name.is_some() { - self.initials(&mut res, Some("."), true).unwrap(); + self.initials(&mut res, Some(". "), true).unwrap(); res.push(' '); } } else if let Some(given_name) = &self.given_name { @@ -536,17 +548,62 @@ mod tests { let mut s = String::new(); let p = Person::from_strings(vec!["Dissmer", "Courtney Deliah"]).unwrap(); p.initials(&mut s, Some("."), true).unwrap(); + assert_eq!("C.D.", s); + + let mut s = String::new(); + let p = Person::from_strings(vec!["Dissmer", "Courtney Deliah"]).unwrap(); + p.initials(&mut s, Some(". "), true).unwrap(); assert_eq!("C. D.", s); + let mut s = String::new(); + let p = Person::from_strings(vec!["Dissmer", "Courtney Deliah"]).unwrap(); + p.initials(&mut s, Some(""), true).unwrap(); + assert_eq!("CD", s); + let mut s = String::new(); let p = Person::from_strings(vec!["Dissmer", "Courtney Deliah"]).unwrap(); p.initials(&mut s, None, true).unwrap(); assert_eq!("CD", s); + let mut s = String::new(); + let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap(); + p.initials(&mut s, Some(". "), true).unwrap(); + assert_eq!("H.-J.", s); + + let mut s = String::new(); + let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap(); + p.initials(&mut s, Some("."), true).unwrap(); + assert_eq!("H.-J.", s); + + let mut s = String::new(); + let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap(); + p.initials(&mut s, Some(""), true).unwrap(); + assert_eq!("H-J", s); + let mut s = String::new(); let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap(); p.initials(&mut s, None, true).unwrap(); assert_eq!("H-J", s); + + let mut s = String::new(); + let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap(); + p.initials(&mut s, Some(". "), false).unwrap(); + assert_eq!("H. J.", s); + + let mut s = String::new(); + let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap(); + p.initials(&mut s, Some("."), false).unwrap(); + assert_eq!("H.J.", s); + + let mut s = String::new(); + let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap(); + p.initials(&mut s, Some(""), false).unwrap(); + assert_eq!("HJ", s); + + let mut s = String::new(); + let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap(); + p.initials(&mut s, None, false).unwrap(); + assert_eq!("HJ", s); } #[test] @@ -562,4 +619,16 @@ mod tests { p.first_name_with_delimiter(&mut s, Some(".")).unwrap(); assert_eq!("James T.", s); } + + #[test] + fn person_name_retrieval_order() { + let p = + Person::from_strings(vec!["van Dissmer", "Jr.", "Courtney Deliah"]).unwrap(); + assert_eq!("van Dissmer, Courtney Deliah, Jr.", p.name_first(false, false)); + assert_eq!("Dissmer, Courtney Deliah van, Jr.", p.name_first(false, true)); + assert_eq!("van Dissmer, C. D., Jr.", p.name_first(true, false)); + assert_eq!("Dissmer, C. D. van, Jr.", p.name_first(true, true)); + assert_eq!("Courtney Deliah van Dissmer Jr.", p.given_first(false)); + assert_eq!("C. D. van Dissmer Jr.", p.given_first(true)); + } } diff --git a/tests/citeproc-pass.txt b/tests/citeproc-pass.txt index 6ae51580..3619e671 100644 --- a/tests/citeproc-pass.txt +++ b/tests/citeproc-pass.txt @@ -188,6 +188,7 @@ name_AsianGlyphs name_AuthorCountWithSameVarContentAndCombinedTermFail name_AuthorCountWithSameVarContentAndCombinedTermSucceed name_CelticClanName +name_CeltsAndToffsNoHyphens name_CeltsAndToffsWithHyphens name_CiteGroupDelimiterWithYearCollapse name_CollapseRoleLabels @@ -206,6 +207,7 @@ name_InstitutionDecoration name_LabelAfterPlural name_LabelAfterPluralDecorations name_LabelFormatBug +name_MultipleLiteral name_NoNameNode name_NonDroppingParticleDefault name_OnlyFamilyname