Skip to content

Make names & emails ASCII-case-insensitive #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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