Skip to content
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

Potential enhancement to add any freetext as display name #15

Closed
wants to merge 4 commits into from
Closed
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
90 changes: 77 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@
/// create an instance. The various components of the email _are not_ parsed out to be accessible
/// independently.
///
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Eq)]
pub struct EmailAddress(String);

// ------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -448,8 +448,8 @@

impl PartialEq for EmailAddress {
fn eq(&self, other: &Self) -> bool {
let (left, right) = split_at(&self.0).unwrap();
let (other_left, other_right) = split_at(&other.0).unwrap();
let (left, right, _) = split_parts(&self.0).unwrap();
let (other_left, other_right, _) = split_parts(&other.0).unwrap();
left.eq(other_left) && right.eq_ignore_ascii_case(other_right)
}
}
Expand Down Expand Up @@ -645,10 +645,48 @@
/// ```
///
pub fn local_part(&self) -> &str {
let (left, _) = split_at(&self.0).unwrap();
let (left, _, _) = split_parts(&self.0).unwrap();
left
}

///
/// Returns the display part of the email address. This is borrowed so that no additional
/// allocation is required.
///
/// ```rust
/// use email_address::*;
/// use std::str::FromStr;
///
/// assert_eq!(
/// EmailAddress::from_str("Name <[email protected]>").unwrap().display_part(),
/// String::from("Name")
/// );
/// ```
///
pub fn display_part(&self) -> &str {
let (_, _, display) = split_parts(&self.0).unwrap();
display
}

///
/// Returns the email part of the email address. This is borrowed so that no additional
/// allocation is required.
///
/// ```rust
/// use email_address::*;
/// use std::str::FromStr;
///
/// assert_eq!(
/// EmailAddress::from_str("Name <[email protected]>").unwrap().email(),
/// String::from("[email protected]")
/// );
/// ```
///
pub fn email(&self) -> String {
let (left, right, _) = split_parts(&self.0).unwrap();
format!("{}@{}", left, right)
}

///
/// Returns the domain of the email address. This is borrowed so that no additional
/// allocation is required.
Expand All @@ -664,7 +702,7 @@
/// ```
///
pub fn domain(&self) -> &str {
let (_, right) = split_at(&self.0).unwrap();
let (_, right, _) = split_parts(&self.0).unwrap();
right
}

Expand Down Expand Up @@ -719,12 +757,31 @@
// Deals with cases of '@' in `local-part`, if it is quoted they are legal, if
// not then they'll return an `InvalidCharacter` error later.
//
let (left, right) = split_at(address)?;
parse_local_part(left, options)?;
parse_domain(right, options)?;
let (local_part, domain, _) = split_parts(address)?;
parse_local_part(local_part)?;
parse_domain(domain)?;
Ok(EmailAddress(address.to_owned()))
}

fn split_parts(address: &str) -> Result<(&str, &str, &str), Error> {
let (display, email) = split_display_email(address)?;
let (local_part, domain) = split_at(email)?;
Ok((local_part, domain, display))
}

fn split_display_email(text: &str) -> Result<(&str, &str), Error> {
match text.rsplit_once(" <") {
None => Ok(("", text)),
Some((left, right)) => {
let right = right.trim();
let email = &right[0..right.len() - 1];
let display_name = left.trim();

Ok((display_name, email))
}
}
}

fn split_at(address: &str) -> Result<(&str, &str), Error> {
match address.rsplit_once(AT) {
None => Error::MissingSeparator.into(),
Expand Down Expand Up @@ -999,7 +1056,10 @@

#[test]
fn test_good_examples_from_wikipedia_09() {
is_valid("admin@mailserver1", Some("local domain name with no TLD, although ICANN highly discourages dotless email addresses"));
is_valid(
"admin@mailserver1",
Some("local domain name with no TLD, although ICANN highly discourages dotless email addresses"),
);
}

#[test]
Expand Down Expand Up @@ -1079,7 +1139,7 @@

#[test]
fn test_good_examples_from_wikipedia_23() {
is_valid("квіточка@пошта.укр", Some("Ukranian"));

Check warning on line 1142 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Ukranian" should be "Ukrainian".
}

#[test]
Expand Down Expand Up @@ -1268,9 +1328,10 @@

#[test]
fn test_bad_examples_from_wikipedia_02() {
expect("a\"b(c)d,e:f;g<h>i[j\\k][email protected]",
expect(
"a\"b(c)d,e:f;g<h>i[j\\k][email protected]",
Error::InvalidCharacter,
Some("none of the special characters in this local-part are allowed outside quotation marks")
Some("none of the special characters in this local-part are allowed outside quotation marks"),
);
}

Expand All @@ -1287,9 +1348,12 @@

#[test]
fn test_bad_examples_from_wikipedia_04() {
expect("this is\"not\\[email protected]",
expect(
"this is\"not\\[email protected]",
Error::InvalidCharacter,
Some("spaces, quotes, and backslashes may only exist when within quoted strings and preceded by a backslash")
Some(
"spaces, quotes, and backslashes may only exist when within quoted strings and preceded by a backslash",
),
);
}

Expand Down Expand Up @@ -1608,4 +1672,4 @@
assert_eq!(email, EmailAddress::new_unchecked("[email protected]"));
assert_eq!(email, EmailAddress::new_unchecked("[email protected]"));
}
}

Check failure on line 1675 in src/lib.rs

View workflow job for this annotation

GitHub Actions / rustfmt

this file contains an unclosed delimiter
Loading