Skip to content

Commit d8fee9a

Browse files
committed
Make names & emails ASCII-case-insensitive
1 parent c548f6b commit d8fee9a

File tree

7 files changed

+80
-63
lines changed

7 files changed

+80
-63
lines changed

Cargo.lock

Lines changed: 8 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ walkdir = "2"
1515
regex = "1.5.5"
1616
mailmap = { path = "./mailmap" }
1717
ureq = { version = "2.6.2", features = ["json"] }
18-
unicase = "2.6.0"
18+
uncased = "0.9.10"
1919

2020
[profile.release]
2121
debug = 2

mailmap/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ categories = ["parsing"]
1111
license = "MIT OR Apache-2.0"
1212

1313
[dependencies]
14-
unicase = "2.6.0"
14+
uncased = "0.9.10"

mailmap/src/lib.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::hash::Hash;
33
use std::pin::Pin;
44
use std::ptr::NonNull;
55

6-
use unicase::UniCase;
6+
use uncased::{Uncased, UncasedStr};
77

88
#[cfg(test)]
99
mod test;
@@ -39,10 +39,10 @@ impl fmt::Debug for Mailmap {
3939

4040
#[derive(Copy, Clone)]
4141
struct RawMapEntry {
42-
canonical_name: Option<NonNull<str>>,
43-
canonical_email: Option<NonNull<str>>,
44-
current_name: Option<NonNull<str>>,
45-
current_email: Option<NonNull<str>>,
42+
canonical_name: Option<NonNull<UncasedStr>>,
43+
canonical_email: Option<NonNull<UncasedStr>>,
44+
current_name: Option<NonNull<UncasedStr>>,
45+
current_email: Option<NonNull<UncasedStr>>,
4646
}
4747

4848
impl RawMapEntry {
@@ -58,10 +58,10 @@ impl RawMapEntry {
5858

5959
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6060
struct MapEntry<'a> {
61-
canonical_name: Option<&'a str>,
62-
canonical_email: Option<&'a str>,
63-
current_name: Option<&'a str>,
64-
current_email: Option<&'a str>,
61+
canonical_name: Option<&'a UncasedStr>,
62+
canonical_email: Option<&'a UncasedStr>,
63+
current_name: Option<&'a UncasedStr>,
64+
current_email: Option<&'a UncasedStr>,
6565
}
6666

6767
impl<'a> MapEntry<'a> {
@@ -77,15 +77,15 @@ impl<'a> MapEntry<'a> {
7777

7878
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
7979
pub struct Author {
80-
pub name: UniCase<String>,
81-
pub email: UniCase<String>,
80+
pub name: Uncased<'static>,
81+
pub email: Uncased<'static>,
8282
}
8383

8484
impl Author {
8585
pub fn new(name: String, email: String) -> Self {
8686
Self {
87-
name: UniCase::new(name),
88-
email: UniCase::new(email),
87+
name: name.into(),
88+
email: email.into(),
8989
}
9090
}
9191
}
@@ -117,17 +117,17 @@ impl Mailmap {
117117
let entry = unsafe { entry.to_entry(&self.buffer) };
118118
if let Some(email) = entry.current_email {
119119
if let Some(name) = entry.current_name {
120-
if author.name == UniCase::new(name) && author.email == UniCase::new(email) {
120+
if author.name == name && author.email == email {
121121
return Author::new(
122-
entry.canonical_name.unwrap_or(&author.name).to_owned(),
123-
entry.canonical_email.expect("canonical email").to_owned(),
122+
entry.canonical_name.unwrap_or(&author.name).to_string(),
123+
entry.canonical_email.expect("canonical email").to_string(),
124124
);
125125
}
126126
} else {
127-
if author.email == UniCase::new(email) {
127+
if author.email == email {
128128
return Author::new(
129-
entry.canonical_name.unwrap_or(&author.name).to_owned(),
130-
entry.canonical_email.expect("canonical email").to_owned(),
129+
entry.canonical_name.unwrap_or(&author.name).to_string(),
130+
entry.canonical_email.expect("canonical email").to_string(),
131131
);
132132
}
133133
}
@@ -138,7 +138,7 @@ impl Mailmap {
138138
}
139139
}
140140

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

154-
fn read_name<'a>(line: &mut &'a str) -> Option<&'a str> {
154+
fn read_name<'a>(line: &mut &'a str) -> Option<&'a UncasedStr> {
155155
let end = if let Some(end) = line.find('<') {
156156
end
157157
} else {
158158
return None;
159159
};
160-
let ret = &line[..end].trim();
160+
let ret = line[..end].trim();
161161
*line = &line[end..];
162162
if ret.is_empty() {
163163
None
164164
} else {
165-
Some(ret)
165+
Some(ret.into())
166166
}
167167
}
168168

mailmap/src/test.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn comment_2() {
2323
fn email_1() {
2424
assert_eq!(
2525
test_parser!(read_email, "<foo@example.com>", ""),
26-
Some("foo@example.com")
26+
Some("foo@example.com".into())
2727
);
2828
}
2929

@@ -35,7 +35,7 @@ fn email_2() {
3535
"<foo@example.com> <foo2@example.com>",
3636
" <foo2@example.com>"
3737
),
38-
Some("foo@example.com")
38+
Some("foo@example.com".into())
3939
);
4040
}
4141

@@ -59,7 +59,7 @@ fn name_1() {
5959
"Canonical Name <foo@example.com>",
6060
"<foo@example.com>"
6161
),
62-
Some("Canonical Name"),
62+
Some("Canonical Name".into()),
6363
);
6464
}
6565

@@ -68,10 +68,10 @@ fn line_1() {
6868
assert_eq!(
6969
parse_line("Joe Bob <email1> <email2>", 0),
7070
Some(MapEntry {
71-
canonical_name: Some("Joe Bob"),
72-
canonical_email: Some("email1"),
71+
canonical_name: Some("Joe Bob".into()),
72+
canonical_email: Some("email1".into()),
7373
current_name: None,
74-
current_email: Some("email2"),
74+
current_email: Some("email2".into()),
7575
})
7676
);
7777
}
@@ -81,18 +81,18 @@ fn line_2() {
8181
assert_eq!(
8282
parse_line("Joe Bob <email1>", 0),
8383
Some(MapEntry {
84-
canonical_name: Some("Joe Bob"),
85-
canonical_email: Some("email1"),
84+
canonical_name: Some("Joe Bob".into()),
85+
canonical_email: Some("email1".into()),
8686
current_name: None,
87-
current_email: Some("email1"),
87+
current_email: Some("email1".into()),
8888
})
8989
);
9090
}
9191

9292
fn a(name: &str, email: &str) -> Author {
9393
Author {
94-
name: name.into(),
95-
email: email.into(),
94+
name: name.to_owned().into(),
95+
email: email.to_owned().into(),
9696
}
9797
}
9898

@@ -123,3 +123,12 @@ fn map_4() {
123123
let mm = map("<PE> <CE>");
124124
assert_eq!(mm.canonicalize(&a("any", "CE")), a("any", "PE"));
125125
}
126+
127+
#[test]
128+
fn case_insensitive() {
129+
let mm = map("Proper Name <proper@email.xx> CoMmIt NaMe <CoMmIt@EmAiL.xX>");
130+
assert_eq!(
131+
mm.canonicalize(&a("Commit Name", "commit@email.xx")),
132+
a("Proper Name", "proper@email.xx")
133+
);
134+
}

src/reviewers.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ impl Reviewers {
2121
}
2222

2323
fn a(name: &str, email: &str) -> AddKind {
24-
AddKind::New(Author {
25-
name: name.into(),
26-
email: email.into(),
27-
})
24+
AddKind::New(Author::new(
25+
name.into(),
26+
email.into(),
27+
))
2828
}
2929

3030
fn alias(name: &'static str) -> AddKind {

src/site.rs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use handlebars::Handlebars;
33
use std::collections::{BTreeMap, HashMap};
44
use std::fs;
55
use std::path::Path;
6-
use unicase::UniCase;
76

87
pub fn render(
98
by_version: BTreeMap<VersionTag, AuthorMap>,
@@ -149,16 +148,16 @@ fn author_map_to_scores(map: &AuthorMap) -> Vec<Entry> {
149148
let scores = map
150149
.iter()
151150
.map(|(author, commits)| {
152-
let name = UniCase::into_inner(author.name.clone());
151+
let name = author.name.to_string();
153152

154153
Entry {
155154
rank: 0,
156155
author: if debug_emails {
157-
format!("{name} ({})", UniCase::into_inner(author.email.clone()))
156+
format!("{name} ({})", author.email)
158157
} else {
159158
name
160159
},
161-
email: UniCase::into_inner(author.email.clone()),
160+
email: author.email.to_string(),
162161
commits,
163162
}
164163
})
@@ -189,21 +188,27 @@ fn author_map_to_scores(map: &AuthorMap) -> Vec<Entry> {
189188
fn deduplicate_scores(entries: Vec<Entry>) -> Vec<Entry> {
190189
let mut entry_map: HashMap<String, Vec<Entry>> = HashMap::with_capacity(entries.len());
191190
for entry in entries {
192-
entry_map.entry(entry.email.clone()).or_default().push(entry);
191+
entry_map
192+
.entry(entry.email.clone())
193+
.or_default()
194+
.push(entry);
193195
}
194196

195-
entry_map.into_values().map(|mut entry| {
196-
// If there are multiple entries with the same maximum commit count, ensure that
197-
// the ordering is stable, by sorting based on the whole entry.
198-
entry.sort();
199-
let canonical_entry = entry.iter().max_by_key(|entry| entry.commits).unwrap();
200-
Entry {
201-
rank: 0,
202-
author: canonical_entry.author.clone(),
203-
email: canonical_entry.email.clone(),
204-
commits: entry.iter().map(|e| e.commits).sum(),
205-
}
206-
}).collect()
197+
entry_map
198+
.into_values()
199+
.map(|mut entry| {
200+
// If there are multiple entries with the same maximum commit count, ensure that
201+
// the ordering is stable, by sorting based on the whole entry.
202+
entry.sort();
203+
let canonical_entry = entry.iter().max_by_key(|entry| entry.commits).unwrap();
204+
Entry {
205+
rank: 0,
206+
author: canonical_entry.author.clone(),
207+
email: canonical_entry.email.clone(),
208+
commits: entry.iter().map(|e| e.commits).sum(),
209+
}
210+
})
211+
.collect()
207212
}
208213

209214
fn releases(

0 commit comments

Comments
 (0)