Skip to content

Commit c13a403

Browse files
authored
Merge pull request #1922 from pierrechevalier83/make_email_with_spaces_roundtrip
fix: Make email with spaces round-trip
2 parents da1545f + 39e35a3 commit c13a403

File tree

5 files changed

+57
-22
lines changed

5 files changed

+57
-22
lines changed

gix-actor/src/lib.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,28 @@ pub mod signature;
2828
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
2929
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3030
pub struct Identity {
31-
/// The actors name.
31+
/// The actors name, potentially with whitespace as parsed.
32+
///
33+
/// Use [IdentityRef::trim()] or trim manually to be able to clean it up.
3234
pub name: BString,
33-
/// The actor's email.
35+
/// The actor's email, potentially with whitespace and garbage as parsed.
36+
///
37+
/// Use [IdentityRef::trim()] or trim manually to be able to clean it up.
3438
pub email: BString,
3539
}
3640

3741
/// A person with name and email, as reference.
3842
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
3943
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
4044
pub struct IdentityRef<'a> {
41-
/// The actors name.
45+
/// The actors name, potentially with whitespace as parsed.
46+
///
47+
/// Use [IdentityRef::trim()] or trim manually to be able to clean it up.
4248
#[cfg_attr(feature = "serde", serde(borrow))]
4349
pub name: &'a BStr,
44-
/// The actor's email.
50+
/// The actor's email, potentially with whitespace and garbage as parsed.
51+
///
52+
/// Use [IdentityRef::trim()] or trim manually to be able to clean it up.
4553
pub email: &'a BStr,
4654
}
4755

@@ -51,9 +59,13 @@ pub struct IdentityRef<'a> {
5159
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
5260
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5361
pub struct Signature {
54-
/// The actors name.
62+
/// The actors name, potentially with whitespace as parsed.
63+
///
64+
/// Use [SignatureRef::trim()] or trim manually to be able to clean it up.
5565
pub name: BString,
56-
/// The actor's email.
66+
/// The actor's email, potentially with whitespace and garbage as parsed.
67+
///
68+
/// Use [SignatureRef::trim()] or trim manually to be able to clean it up.
5769
pub email: BString,
5870
/// The time stamp at which the signature is performed.
5971
pub time: Time,
@@ -65,10 +77,14 @@ pub struct Signature {
6577
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
6678
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6779
pub struct SignatureRef<'a> {
68-
/// The actor's name.
80+
/// The actors name, potentially with whitespace as parsed.
81+
///
82+
/// Use [SignatureRef::trim()] or trim manually to be able to clean it up.
6983
#[cfg_attr(feature = "serde", serde(borrow))]
7084
pub name: &'a BStr,
71-
/// The actor's email.
85+
/// The actor's email, potentially with whitespace and garbage as parsed.
86+
///
87+
/// Use [SignatureRef::trim()] or trim manually to be able to clean it up.
7288
pub email: &'a BStr,
7389
/// The time stamp at which the signature was performed.
7490
pub time: gix_date::Time,

gix-actor/src/signature/decode.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,22 +79,15 @@ pub(crate) mod function {
7979
StrContext::Label("Closing '>' not found"),
8080
)))?;
8181
let i_name_and_email = &i[..right_delim_idx];
82-
let skip_from_right = i_name_and_email
83-
.iter()
84-
.rev()
85-
.take_while(|b| b.is_ascii_whitespace() || **b == b'>')
86-
.count();
82+
let skip_from_right = i_name_and_email.iter().rev().take_while(|b| **b == b'>').count();
8783
let left_delim_idx = i_name_and_email
8884
.find_byte(b'<')
8985
.ok_or(ErrMode::Cut(E::from_input(i).add_context(
9086
&i_name_and_email,
9187
&start,
9288
StrContext::Label("Opening '<' not found"),
9389
)))?;
94-
let skip_from_left = i[left_delim_idx..]
95-
.iter()
96-
.take_while(|b| b.is_ascii_whitespace() || **b == b'<')
97-
.count();
90+
let skip_from_left = i[left_delim_idx..].iter().take_while(|b| **b == b'<').count();
9891
let mut name = i[..left_delim_idx].as_bstr();
9992
name = name.strip_suffix(b" ").unwrap_or(name).as_bstr();
10093

@@ -164,6 +157,17 @@ mod tests {
164157
);
165158
}
166159

160+
#[test]
161+
fn email_with_space() {
162+
assert_eq!(
163+
decode
164+
.parse_peek(b"Sebastian Thiel <\t[email protected] > 1528473343 +0230")
165+
.expect("parse to work")
166+
.1,
167+
signature("Sebastian Thiel", "\t[email protected] ", 1528473343, Sign::Plus, 9000)
168+
);
169+
}
170+
167171
#[test]
168172
fn negative_offset_0000() {
169173
assert_eq!(

gix-actor/tests/identity/mod.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ use gix_actor::Identity;
55
fn round_trip() -> gix_testtools::Result {
66
static DEFAULTS: &[&[u8]] = &[
77
b"Sebastian Thiel <[email protected]>",
8+
b"Sebastian Thiel < [email protected]>",
9+
b"Sebastian Thiel <[email protected] >",
10+
b"Sebastian Thiel <\t[email protected] \t >",
811
".. ☺️Sebastian 王知明 Thiel🙌 .. <[email protected]>".as_bytes(),
912
b".. whitespace \t is explicitly allowed - unicode aware trimming must be done elsewhere <[email protected]>"
1013
];
@@ -19,15 +22,21 @@ fn round_trip() -> gix_testtools::Result {
1922

2023
#[test]
2124
fn lenient_parsing() -> gix_testtools::Result {
22-
for input in [
23-
"First Last<<fl <First Last<[email protected] >> >",
24-
"First Last<fl <First Last<[email protected]>>\n",
25+
for (input, expected_email) in [
26+
(
27+
"First Last<<fl <First Last<[email protected] >> >",
28+
"fl <First Last<[email protected] >> ",
29+
),
30+
(
31+
"First Last<fl <First Last<[email protected]>>\n",
32+
"fl <First Last<[email protected]",
33+
),
2534
] {
2635
let identity = gix_actor::IdentityRef::from_bytes::<()>(input.as_bytes()).unwrap();
2736
assert_eq!(identity.name, "First Last");
2837
assert_eq!(
29-
identity.email, "fl <First Last<[email protected]",
30-
"extra trailing and leading angled parens are stripped"
38+
identity.email, expected_email,
39+
"emails are parsed but left as is for round-tripping"
3140
);
3241
let signature: Identity = identity.into();
3342
let mut output = Vec::new();
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
tree 1b2dfb4ac5e42080b682fc676e9738c94ce6d54d
2+
author Sebastian Thiel <[email protected] > 1592437401 +0800
3+
committer Sebastian Thiel <[email protected]> 1592437401 +0800
4+
5+
In the author line, the email is followed by a space

gix-object/tests/object/encode/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ mod commit {
8787
round_trip!(
8888
gix_object::Commit,
8989
gix_object::CommitRef,
90+
"commit/email-with-space.txt",
9091
"commit/signed-whitespace.txt",
9192
"commit/two-multiline-headers.txt",
9293
"commit/mergetag.txt",

0 commit comments

Comments
 (0)