Skip to content

Commit 1890db7

Browse files
committed
sketch Snapshot API to implement map building and signature resolution (#366)
1 parent 85e3820 commit 1890db7

File tree

7 files changed

+100
-17
lines changed

7 files changed

+100
-17
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

git-mailmap/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ doctest = false
1212

1313
[features]
1414
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
15-
serde1 = ["serde", "bstr/serde1"]
15+
serde1 = ["serde", "bstr/serde1", "git-actor/serde1"]
1616

1717
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1818

1919
[dependencies]
20+
git-actor = { version = "^0.8.1", path = "../git-actor" }
2021
bstr = { version = "0.2.13", default-features = false, features = ["std", "unicode"]}
2122
quick-error = "2.0.0"
2223
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]}

git-mailmap/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ pub fn parse(buf: &[u8]) -> parse::Lines<'_> {
88
parse::Lines::new(buf)
99
}
1010

11+
pub fn parse_ignore_errors(buf: &[u8]) -> impl Iterator<Item = Entry<'_>> {
12+
parse(buf).filter_map(Result::ok)
13+
}
14+
1115
mod entry;
1216

1317
mod snapshot;

git-mailmap/src/snapshot.rs

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use bstr::{BString, ByteSlice};
1+
use crate::Snapshot;
2+
use bstr::{BStr, BString, ByteSlice};
23
use std::cmp::Ordering;
34
use std::ops::Deref;
45

@@ -9,6 +10,36 @@ enum EncodedString {
910
Unknown(BString),
1011
}
1112

13+
impl EncodedString {
14+
fn cmp_ref(&self, other: EncodedStringRef<'_>) -> Ordering {
15+
match (self, other) {
16+
(EncodedString::Utf8(a), EncodedStringRef::Utf8(b)) => {
17+
let a = a.chars().map(|c| c.to_ascii_lowercase());
18+
let b = b.chars().map(|c| c.to_ascii_lowercase());
19+
a.cmp(b)
20+
}
21+
(EncodedString::Unknown(a), EncodedStringRef::Unknown(b)) => a.deref().as_bstr().cmp(b),
22+
(EncodedString::Utf8(a), EncodedStringRef::Unknown(b)) => a.as_bytes().cmp(b.as_ref()),
23+
(EncodedString::Unknown(a), EncodedStringRef::Utf8(b)) => a.deref().as_bytes().cmp(b.as_bytes()),
24+
}
25+
}
26+
}
27+
28+
#[cfg_attr(test, derive(Debug))]
29+
enum EncodedStringRef<'a> {
30+
Utf8(&'a str),
31+
Unknown(&'a BStr),
32+
}
33+
34+
impl EncodedString {
35+
fn to_ref(&self) -> EncodedStringRef<'_> {
36+
match self {
37+
EncodedString::Unknown(v) => EncodedStringRef::Unknown(v.deref().as_bstr()),
38+
EncodedString::Utf8(v) => EncodedStringRef::Utf8(v),
39+
}
40+
}
41+
}
42+
1243
impl Eq for EncodedString {}
1344

1445
impl PartialEq<Self> for EncodedString {
@@ -25,22 +56,12 @@ impl PartialOrd<Self> for EncodedString {
2556

2657
impl Ord for EncodedString {
2758
fn cmp(&self, other: &Self) -> Ordering {
28-
use EncodedString::*;
29-
match (self, other) {
30-
(Utf8(a), Utf8(b)) => {
31-
let a = a.chars().map(|c| c.to_ascii_lowercase());
32-
let b = b.chars().map(|c| c.to_ascii_lowercase());
33-
a.cmp(b)
34-
}
35-
(Unknown(a), Unknown(b)) => a.cmp(b),
36-
(Utf8(a), Unknown(b)) => a.as_bytes().cmp(b.as_ref()),
37-
(Unknown(a), Utf8(b)) => a.deref().as_bytes().cmp(b.as_bytes()),
38-
}
59+
self.cmp_ref(other.to_ref())
3960
}
4061
}
4162

4263
#[derive(Clone)]
43-
pub(crate) struct NameEntry {
64+
struct NameEntry {
4465
new_name: Option<BString>,
4566
new_email: Option<BString>,
4667
old_name: EncodedString,
@@ -55,6 +76,30 @@ pub(crate) struct EmailEntry {
5576
entries_by_old_name: Vec<NameEntry>,
5677
}
5778

79+
impl Snapshot {
80+
pub fn from_bytes(buf: &[u8]) -> Self {
81+
Self::new(crate::parse_ignore_errors(buf))
82+
}
83+
84+
pub fn new<'a>(entries: impl IntoIterator<Item = crate::Entry<'a>>) -> Self {
85+
let mut snapshot = Self::default();
86+
snapshot.extend(entries);
87+
snapshot
88+
}
89+
90+
pub fn extend<'a>(&mut self, entries: impl IntoIterator<Item = crate::Entry<'a>>) -> &mut Self {
91+
todo!()
92+
}
93+
94+
pub fn try_resolve(&self, signature: &git_actor::SignatureRef<'_>) -> Option<git_actor::Signature> {
95+
todo!()
96+
}
97+
98+
pub fn resolve(&self, signature: &git_actor::SignatureRef<'_>) -> git_actor::Signature {
99+
self.try_resolve(signature).unwrap_or_else(|| signature.to_owned())
100+
}
101+
}
102+
58103
#[cfg(test)]
59104
mod encoded_string {
60105
use crate::snapshot::EncodedString;

git-mailmap/tests/mailmap.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
mod parse;
2+
mod snapshot;

git-mailmap/tests/parse/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use git_mailmap::{parse, Entry};
2-
use git_testtools::fixture_path;
2+
use git_testtools::fixture_bytes;
33

44
#[test]
55
fn line_numbers_are_counted_correctly_in_errors() {
6-
let input = std::fs::read(fixture_path("invalid.txt")).unwrap();
6+
let input = fixture_bytes("invalid.txt");
77
let mut actual = git_mailmap::parse(&input).collect::<Vec<_>>().into_iter();
88
assert_eq!(actual.len(), 2);
99

@@ -16,7 +16,7 @@ fn line_numbers_are_counted_correctly_in_errors() {
1616

1717
#[test]
1818
fn a_typical_mailmap() {
19-
let input = std::fs::read(fixture_path("typical.txt")).unwrap();
19+
let input = fixture_bytes("typical.txt");
2020
let actual = git_mailmap::parse(&input).map(Result::unwrap).collect::<Vec<_>>();
2121
assert_eq!(
2222
actual,

git-mailmap/tests/snapshot/mod.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use git_mailmap::Snapshot;
2+
use git_testtools::fixture_bytes;
3+
4+
#[test]
5+
#[ignore]
6+
fn try_resolve() {
7+
let snapshot = snapshot();
8+
let sig = signature("Foo", "Joe@example.com");
9+
assert_eq!(
10+
snapshot.try_resolve(&sig.to_ref()),
11+
Some(signature("Joe R. Developer", "Joe@example.com")),
12+
"resolved signatures contain all original fields, but normalizes only what's in the mapping, lookup is case-insensitive"
13+
);
14+
}
15+
16+
fn snapshot() -> Snapshot {
17+
Snapshot::from_bytes(&fixture_bytes("typical.txt"))
18+
}
19+
20+
fn signature(name: &str, email: &str) -> git_actor::Signature {
21+
git_actor::Signature {
22+
name: name.into(),
23+
email: email.into(),
24+
time: git_actor::Time {
25+
// marker
26+
time: 42,
27+
offset: 53,
28+
sign: git_actor::Sign::Minus,
29+
},
30+
}
31+
}

0 commit comments

Comments
 (0)