Skip to content

Commit 38a56c8

Browse files
authored
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
1 parent 0ee7bd0 commit 38a56c8

File tree

2 files changed

+82
-11
lines changed

2 files changed

+82
-11
lines changed

src/types/persons.rs

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,14 @@ impl Person {
234234
/// Formats the given name into initials.
235235
///
236236
/// For example, `"Judith Beatrice"` would yield `"J. B."` if the
237-
/// `delimiter` argument is set to `Some(".")`, `"Klaus-Peter"` would become
238-
/// `"K-P"` without a delimiter.
237+
/// `delimiter` argument is set to `Some(". ")`, `"Klaus-Peter"` would
238+
/// become `"K-P"` without a delimiter (or with an empty delimiter).
239+
///
240+
/// Whitespace at the end of the delimiter is kept when parts are separated
241+
/// by whitespace (as in the first example), but not when parts of a
242+
/// compound given name are separated by hyphens (for example,
243+
/// `"Klaus-Peter"` produces `"K.-P."` instead of `"K. -P."` for a
244+
/// delimiter of `Some(". ")`).
239245
pub fn initials(
240246
&self,
241247
buf: &mut impl std::fmt::Write,
@@ -253,15 +259,21 @@ impl Person {
253259
if let Some(c) = gr.chars().next() {
254260
if c.is_whitespace() || c == '-' {
255261
if !collect {
262+
let hyphenate = with_hyphen && c == '-';
256263
if let Some(delimiter) = delimiter {
257-
buf.write_str(delimiter.trim_end())?;
264+
// Use the given delimiter, including any spaces at
265+
// its end if there was a whitespace, but not if we
266+
// should add a hyphen in a compound given name.
267+
buf.write_str(if hyphenate {
268+
delimiter.trim_end()
269+
} else {
270+
delimiter
271+
})?;
258272
}
259273

260274
collect = true;
261-
if with_hyphen && c == '-' {
262-
buf.write_char(c)?;
263-
} else if delimiter.is_some() {
264-
buf.write_char(' ')?;
275+
if hyphenate {
276+
buf.write_char('-')?;
265277
}
266278
}
267279
continue;
@@ -277,7 +289,7 @@ impl Person {
277289

278290
if non_empty && !collect {
279291
if let Some(delim) = delimiter {
280-
buf.write_str(delim)?;
292+
buf.write_str(delim.trim_end())?;
281293
}
282294
}
283295

@@ -315,7 +327,7 @@ impl Person {
315327
Ok(())
316328
}
317329

318-
/// Get the name with the family name fist, the initials
330+
/// Get the name with the family name first, the initials
319331
/// afterwards, separated by a comma.
320332
pub fn name_first(&self, initials: bool, prefix_given_name: bool) -> String {
321333
let mut res = if !prefix_given_name {
@@ -331,7 +343,7 @@ impl Person {
331343
if initials {
332344
if self.given_name.is_some() {
333345
res += ", ";
334-
self.initials(&mut res, Some("."), true).unwrap();
346+
self.initials(&mut res, Some(". "), true).unwrap();
335347
}
336348
} else if let Some(given_name) = &self.given_name {
337349
res += ", ";
@@ -361,7 +373,7 @@ impl Person {
361373

362374
if initials {
363375
if self.given_name.is_some() {
364-
self.initials(&mut res, Some("."), true).unwrap();
376+
self.initials(&mut res, Some(". "), true).unwrap();
365377
res.push(' ');
366378
}
367379
} else if let Some(given_name) = &self.given_name {
@@ -536,17 +548,62 @@ mod tests {
536548
let mut s = String::new();
537549
let p = Person::from_strings(vec!["Dissmer", "Courtney Deliah"]).unwrap();
538550
p.initials(&mut s, Some("."), true).unwrap();
551+
assert_eq!("C.D.", s);
552+
553+
let mut s = String::new();
554+
let p = Person::from_strings(vec!["Dissmer", "Courtney Deliah"]).unwrap();
555+
p.initials(&mut s, Some(". "), true).unwrap();
539556
assert_eq!("C. D.", s);
540557

558+
let mut s = String::new();
559+
let p = Person::from_strings(vec!["Dissmer", "Courtney Deliah"]).unwrap();
560+
p.initials(&mut s, Some(""), true).unwrap();
561+
assert_eq!("CD", s);
562+
541563
let mut s = String::new();
542564
let p = Person::from_strings(vec!["Dissmer", "Courtney Deliah"]).unwrap();
543565
p.initials(&mut s, None, true).unwrap();
544566
assert_eq!("CD", s);
545567

568+
let mut s = String::new();
569+
let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap();
570+
p.initials(&mut s, Some(". "), true).unwrap();
571+
assert_eq!("H.-J.", s);
572+
573+
let mut s = String::new();
574+
let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap();
575+
p.initials(&mut s, Some("."), true).unwrap();
576+
assert_eq!("H.-J.", s);
577+
578+
let mut s = String::new();
579+
let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap();
580+
p.initials(&mut s, Some(""), true).unwrap();
581+
assert_eq!("H-J", s);
582+
546583
let mut s = String::new();
547584
let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap();
548585
p.initials(&mut s, None, true).unwrap();
549586
assert_eq!("H-J", s);
587+
588+
let mut s = String::new();
589+
let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap();
590+
p.initials(&mut s, Some(". "), false).unwrap();
591+
assert_eq!("H. J.", s);
592+
593+
let mut s = String::new();
594+
let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap();
595+
p.initials(&mut s, Some("."), false).unwrap();
596+
assert_eq!("H.J.", s);
597+
598+
let mut s = String::new();
599+
let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap();
600+
p.initials(&mut s, Some(""), false).unwrap();
601+
assert_eq!("HJ", s);
602+
603+
let mut s = String::new();
604+
let p = Person::from_strings(vec!["Günther", "Hans-Joseph"]).unwrap();
605+
p.initials(&mut s, None, false).unwrap();
606+
assert_eq!("HJ", s);
550607
}
551608

552609
#[test]
@@ -562,4 +619,16 @@ mod tests {
562619
p.first_name_with_delimiter(&mut s, Some(".")).unwrap();
563620
assert_eq!("James T.", s);
564621
}
622+
623+
#[test]
624+
fn person_name_retrieval_order() {
625+
let p =
626+
Person::from_strings(vec!["van Dissmer", "Jr.", "Courtney Deliah"]).unwrap();
627+
assert_eq!("van Dissmer, Courtney Deliah, Jr.", p.name_first(false, false));
628+
assert_eq!("Dissmer, Courtney Deliah van, Jr.", p.name_first(false, true));
629+
assert_eq!("van Dissmer, C. D., Jr.", p.name_first(true, false));
630+
assert_eq!("Dissmer, C. D. van, Jr.", p.name_first(true, true));
631+
assert_eq!("Courtney Deliah van Dissmer Jr.", p.given_first(false));
632+
assert_eq!("C. D. van Dissmer Jr.", p.given_first(true));
633+
}
565634
}

tests/citeproc-pass.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ name_AsianGlyphs
188188
name_AuthorCountWithSameVarContentAndCombinedTermFail
189189
name_AuthorCountWithSameVarContentAndCombinedTermSucceed
190190
name_CelticClanName
191+
name_CeltsAndToffsNoHyphens
191192
name_CeltsAndToffsWithHyphens
192193
name_CiteGroupDelimiterWithYearCollapse
193194
name_CollapseRoleLabels
@@ -206,6 +207,7 @@ name_InstitutionDecoration
206207
name_LabelAfterPlural
207208
name_LabelAfterPluralDecorations
208209
name_LabelFormatBug
210+
name_MultipleLiteral
209211
name_NoNameNode
210212
name_NonDroppingParticleDefault
211213
name_OnlyFamilyname

0 commit comments

Comments
 (0)