From 0574a0ff59dcd8750e80391f09fa4f9b2a1bb161 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Wed, 14 Aug 2024 17:18:50 +0300 Subject: [PATCH] Make names & emails ASCII-case-insensitive --- Cargo.lock | 18 +++++++++++++++ mailmap/Cargo.toml | 1 + mailmap/src/lib.rs | 56 ++++++++++++++++++++++++++++++--------------- mailmap/src/test.rs | 31 ++++++++++++++++--------- src/main.rs | 10 ++++---- src/reviewers.rs | 4 ++-- src/site.rs | 2 +- 7 files changed, 84 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6259aac4..6d4f598a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,6 +236,9 @@ dependencies = [ [[package]] name = "mailmap" version = "0.1.0" +dependencies = [ + "uncased", +] [[package]] name = "maplit" @@ -571,6 +574,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.5" @@ -613,6 +625,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "walkdir" version = "2.3.2" diff --git a/mailmap/Cargo.toml b/mailmap/Cargo.toml index 8d2a1a40..dd3a0d39 100644 --- a/mailmap/Cargo.toml +++ b/mailmap/Cargo.toml @@ -11,3 +11,4 @@ categories = ["parsing"] license = "MIT OR Apache-2.0" [dependencies] +uncased = "0.9.10" diff --git a/mailmap/src/lib.rs b/mailmap/src/lib.rs index c3984464..92d96548 100644 --- a/mailmap/src/lib.rs +++ b/mailmap/src/lib.rs @@ -2,6 +2,8 @@ use std::fmt; use std::pin::Pin; use std::ptr::NonNull; +use uncased::{Uncased, UncasedStr}; + #[cfg(test)] mod test; @@ -36,10 +38,10 @@ impl fmt::Debug for Mailmap { #[derive(Copy, Clone)] struct RawMapEntry { - canonical_name: Option>, - canonical_email: Option>, - current_name: Option>, - current_email: Option>, + canonical_name: Option>, + canonical_email: Option>, + current_name: Option>, + current_email: Option>, } impl RawMapEntry { @@ -55,10 +57,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> { @@ -74,8 +76,8 @@ impl<'a> MapEntry<'a> { #[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] pub struct Author { - pub name: String, - pub email: String, + pub name: Uncased<'static>, + pub email: Uncased<'static>, } impl fmt::Debug for Author { @@ -107,15 +109,31 @@ impl Mailmap { if let Some(name) = entry.current_name { if author.name == name && author.email == email { return Author { - name: entry.canonical_name.unwrap_or(&author.name).to_owned(), - email: entry.canonical_email.expect("canonical email").to_owned(), + name: entry + .canonical_name + .unwrap_or(&author.name) + .to_string() + .into(), + email: entry + .canonical_email + .expect("canonical email") + .to_string() + .into(), }; } } else { if author.email == email { return Author { - name: entry.canonical_name.unwrap_or(&author.name).to_owned(), - email: entry.canonical_email.expect("canonical email").to_owned(), + name: entry + .canonical_name + .unwrap_or(&author.name) + .to_string() + .into(), + email: entry + .canonical_email + .expect("canonical email") + .to_string() + .into(), }; } } @@ -126,7 +144,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; } @@ -136,21 +154,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()) } } diff --git a/mailmap/src/test.rs b/mailmap/src/test.rs index a6407545..c58b9c2a 100644 --- a/mailmap/src/test.rs +++ b/mailmap/src/test.rs @@ -23,7 +23,7 @@ fn comment_2() { fn email_1() { assert_eq!( test_parser!(read_email, "", ""), - Some("foo@example.com") + Some("foo@example.com".into()) ); } @@ -35,7 +35,7 @@ fn email_2() { " ", " " ), - Some("foo@example.com") + Some("foo@example.com".into()) ); } @@ -59,7 +59,7 @@ fn name_1() { "Canonical Name ", "" ), - Some("Canonical Name"), + Some("Canonical Name".into()), ); } @@ -68,10 +68,10 @@ fn line_1() { assert_eq!( parse_line("Joe Bob ", 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()), }) ); } @@ -81,18 +81,18 @@ fn line_2() { assert_eq!( parse_line("Joe Bob ", 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(), } } @@ -123,3 +123,12 @@ fn map_4() { let mm = map(" "); assert_eq!(mm.canonicalize(&a("any", "CE")), a("any", "PE")); } + +#[test] +fn case_insensitive() { + let mm = map("Proper Name CoMmIt NaMe "); + assert_eq!( + mm.canonicalize(&a("Commit Name", "commit@email.xx")), + a("Proper Name", "proper@email.xx") + ); +} diff --git a/src/main.rs b/src/main.rs index 87db9ff9..511b6ca7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,8 +31,8 @@ impl ToAuthor for Author { .unwrap_or_else(|| panic!("no email for {}", sig)); Author { - name: name.to_string(), - email: email.to_string(), + name: name.to_string().into(), + email: email.to_string().into(), } } } @@ -259,8 +259,8 @@ fn commit_coauthors(commit: &Commit) -> Vec { if line.starts_with("Co-authored-by") { if let Some(caps) = RE.captures(line) { coauthors.push(Author { - name: caps["name"].to_string(), - email: caps["email"].to_string(), + name: caps["name"].to_string().into(), + email: caps["email"].to_string().into(), }); } } @@ -589,7 +589,7 @@ fn main() { eprintln!("\tcaused by: {}", cause); cur = cause; } - std::mem::drop(cur); + let _ = cur; std::process::exit(1); } } diff --git a/src/reviewers.rs b/src/reviewers.rs index 419c92a3..7d522c7c 100644 --- a/src/reviewers.rs +++ b/src/reviewers.rs @@ -15,8 +15,8 @@ impl Reviewers { fn a(name: &str, email: &str) -> Author { Author { - name: name.into(), - email: email.into(), + name: name.to_string().into(), + email: email.to_string().into(), } } diff --git a/src/site.rs b/src/site.rs index 47649d34..3f5ebd54 100644 --- a/src/site.rs +++ b/src/site.rs @@ -146,7 +146,7 @@ fn author_map_to_scores(map: &AuthorMap) -> Vec { .iter() .map(|(author, commits)| Entry { rank: 0, - author: author.name.clone(), + author: author.name.to_string(), commits: commits, }) .collect::>();