Skip to content

Commit

Permalink
Make names & emails ASCII-case-insensitive
Browse files Browse the repository at this point in the history
  • Loading branch information
GrigorenkoPV committed Jan 29, 2025
1 parent c548f6b commit d8fee9a
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 63 deletions.
13 changes: 8 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ walkdir = "2"
regex = "1.5.5"
mailmap = { path = "./mailmap" }
ureq = { version = "2.6.2", features = ["json"] }
unicase = "2.6.0"
uncased = "0.9.10"

[profile.release]
debug = 2
Expand Down
2 changes: 1 addition & 1 deletion mailmap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ categories = ["parsing"]
license = "MIT OR Apache-2.0"

[dependencies]
unicase = "2.6.0"
uncased = "0.9.10"
48 changes: 24 additions & 24 deletions mailmap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::hash::Hash;
use std::pin::Pin;
use std::ptr::NonNull;

use unicase::UniCase;
use uncased::{Uncased, UncasedStr};

#[cfg(test)]
mod test;
Expand Down Expand Up @@ -39,10 +39,10 @@ impl fmt::Debug for Mailmap {

#[derive(Copy, Clone)]
struct RawMapEntry {
canonical_name: Option<NonNull<str>>,
canonical_email: Option<NonNull<str>>,
current_name: Option<NonNull<str>>,
current_email: Option<NonNull<str>>,
canonical_name: Option<NonNull<UncasedStr>>,
canonical_email: Option<NonNull<UncasedStr>>,
current_name: Option<NonNull<UncasedStr>>,
current_email: Option<NonNull<UncasedStr>>,
}

impl RawMapEntry {
Expand All @@ -58,10 +58,10 @@ impl RawMapEntry {

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct MapEntry<'a> {
canonical_name: Option<&'a str>,
canonical_email: Option<&'a str>,
current_name: Option<&'a str>,
current_email: Option<&'a str>,
canonical_name: Option<&'a UncasedStr>,
canonical_email: Option<&'a UncasedStr>,
current_name: Option<&'a UncasedStr>,
current_email: Option<&'a UncasedStr>,
}

impl<'a> MapEntry<'a> {
Expand All @@ -77,15 +77,15 @@ impl<'a> MapEntry<'a> {

#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct Author {
pub name: UniCase<String>,
pub email: UniCase<String>,
pub name: Uncased<'static>,
pub email: Uncased<'static>,
}

impl Author {
pub fn new(name: String, email: String) -> Self {
Self {
name: UniCase::new(name),
email: UniCase::new(email),
name: name.into(),
email: email.into(),
}
}
}
Expand Down Expand Up @@ -117,17 +117,17 @@ impl Mailmap {
let entry = unsafe { entry.to_entry(&self.buffer) };
if let Some(email) = entry.current_email {
if let Some(name) = entry.current_name {
if author.name == UniCase::new(name) && author.email == UniCase::new(email) {
if author.name == name && author.email == email {
return Author::new(
entry.canonical_name.unwrap_or(&author.name).to_owned(),
entry.canonical_email.expect("canonical email").to_owned(),
entry.canonical_name.unwrap_or(&author.name).to_string(),
entry.canonical_email.expect("canonical email").to_string(),
);
}
} else {
if author.email == UniCase::new(email) {
if author.email == email {
return Author::new(
entry.canonical_name.unwrap_or(&author.name).to_owned(),
entry.canonical_email.expect("canonical email").to_owned(),
entry.canonical_name.unwrap_or(&author.name).to_string(),
entry.canonical_email.expect("canonical email").to_string(),
);
}
}
Expand All @@ -138,7 +138,7 @@ impl Mailmap {
}
}

fn read_email<'a>(line: &mut &'a str) -> Option<&'a str> {
fn read_email<'a>(line: &mut &'a str) -> Option<&'a UncasedStr> {
if !line.starts_with('<') {
return None;
}
Expand All @@ -148,21 +148,21 @@ fn read_email<'a>(line: &mut &'a str) -> Option<&'a str> {
.unwrap_or_else(|| panic!("could not find email end in {:?}", line));
let ret = &line[1..end];
*line = &line[end + 1..];
Some(ret)
Some(ret.into())
}

fn read_name<'a>(line: &mut &'a str) -> Option<&'a str> {
fn read_name<'a>(line: &mut &'a str) -> Option<&'a UncasedStr> {
let end = if let Some(end) = line.find('<') {
end
} else {
return None;
};
let ret = &line[..end].trim();
let ret = line[..end].trim();
*line = &line[end..];
if ret.is_empty() {
None
} else {
Some(ret)
Some(ret.into())
}
}

Expand Down
31 changes: 20 additions & 11 deletions mailmap/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn comment_2() {
fn email_1() {
assert_eq!(
test_parser!(read_email, "<[email protected]>", ""),
Some("[email protected]")
Some("[email protected]".into())
);
}

Expand All @@ -35,7 +35,7 @@ fn email_2() {
"<[email protected]> <[email protected]>",
" <[email protected]>"
),
Some("[email protected]")
Some("[email protected]".into())
);
}

Expand All @@ -59,7 +59,7 @@ fn name_1() {
"Canonical Name <[email protected]>",
"<[email protected]>"
),
Some("Canonical Name"),
Some("Canonical Name".into()),
);
}

Expand All @@ -68,10 +68,10 @@ fn line_1() {
assert_eq!(
parse_line("Joe Bob <email1> <email2>", 0),
Some(MapEntry {
canonical_name: Some("Joe Bob"),
canonical_email: Some("email1"),
canonical_name: Some("Joe Bob".into()),
canonical_email: Some("email1".into()),
current_name: None,
current_email: Some("email2"),
current_email: Some("email2".into()),
})
);
}
Expand All @@ -81,18 +81,18 @@ fn line_2() {
assert_eq!(
parse_line("Joe Bob <email1>", 0),
Some(MapEntry {
canonical_name: Some("Joe Bob"),
canonical_email: Some("email1"),
canonical_name: Some("Joe Bob".into()),
canonical_email: Some("email1".into()),
current_name: None,
current_email: Some("email1"),
current_email: Some("email1".into()),
})
);
}

fn a(name: &str, email: &str) -> Author {
Author {
name: name.into(),
email: email.into(),
name: name.to_owned().into(),
email: email.to_owned().into(),
}
}

Expand Down Expand Up @@ -123,3 +123,12 @@ fn map_4() {
let mm = map("<PE> <CE>");
assert_eq!(mm.canonicalize(&a("any", "CE")), a("any", "PE"));
}

#[test]
fn case_insensitive() {
let mm = map("Proper Name <[email protected]> CoMmIt NaMe <[email protected]>");
assert_eq!(
mm.canonicalize(&a("Commit Name", "[email protected]")),
a("Proper Name", "[email protected]")
);
}
8 changes: 4 additions & 4 deletions src/reviewers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ impl Reviewers {
}

fn a(name: &str, email: &str) -> AddKind {
AddKind::New(Author {
name: name.into(),
email: email.into(),
})
AddKind::New(Author::new(
name.into(),
email.into(),
))
}

fn alias(name: &'static str) -> AddKind {
Expand Down
39 changes: 22 additions & 17 deletions src/site.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use handlebars::Handlebars;
use std::collections::{BTreeMap, HashMap};
use std::fs;
use std::path::Path;
use unicase::UniCase;

pub fn render(
by_version: BTreeMap<VersionTag, AuthorMap>,
Expand Down Expand Up @@ -149,16 +148,16 @@ fn author_map_to_scores(map: &AuthorMap) -> Vec<Entry> {
let scores = map
.iter()
.map(|(author, commits)| {
let name = UniCase::into_inner(author.name.clone());
let name = author.name.to_string();

Entry {
rank: 0,
author: if debug_emails {
format!("{name} ({})", UniCase::into_inner(author.email.clone()))
format!("{name} ({})", author.email)
} else {
name
},
email: UniCase::into_inner(author.email.clone()),
email: author.email.to_string(),
commits,
}
})
Expand Down Expand Up @@ -189,21 +188,27 @@ fn author_map_to_scores(map: &AuthorMap) -> Vec<Entry> {
fn deduplicate_scores(entries: Vec<Entry>) -> Vec<Entry> {
let mut entry_map: HashMap<String, Vec<Entry>> = HashMap::with_capacity(entries.len());
for entry in entries {
entry_map.entry(entry.email.clone()).or_default().push(entry);
entry_map
.entry(entry.email.clone())
.or_default()
.push(entry);
}

entry_map.into_values().map(|mut entry| {
// If there are multiple entries with the same maximum commit count, ensure that
// the ordering is stable, by sorting based on the whole entry.
entry.sort();
let canonical_entry = entry.iter().max_by_key(|entry| entry.commits).unwrap();
Entry {
rank: 0,
author: canonical_entry.author.clone(),
email: canonical_entry.email.clone(),
commits: entry.iter().map(|e| e.commits).sum(),
}
}).collect()
entry_map
.into_values()
.map(|mut entry| {
// If there are multiple entries with the same maximum commit count, ensure that
// the ordering is stable, by sorting based on the whole entry.
entry.sort();
let canonical_entry = entry.iter().max_by_key(|entry| entry.commits).unwrap();
Entry {
rank: 0,
author: canonical_entry.author.clone(),
email: canonical_entry.email.clone(),
commits: entry.iter().map(|e| e.commits).sum(),
}
})
.collect()
}

fn releases(
Expand Down

0 comments on commit d8fee9a

Please sign in to comment.