111 lines
2.3 KiB
Rust
111 lines
2.3 KiB
Rust
use std::{fmt, str::FromStr};
|
|
|
|
use email_address::{EmailAddress, Options};
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct Email(String);
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum EmailError {
|
|
Empty,
|
|
InvalidUtf8,
|
|
InvalidFormat,
|
|
}
|
|
|
|
impl Email {
|
|
pub fn new(s: impl AsRef<str>) -> Result<Self, EmailError> {
|
|
let s = s.as_ref().trim();
|
|
if s.is_empty() {
|
|
return Err(EmailError::Empty);
|
|
}
|
|
|
|
// Strict "address only" validation (no "Name <a@b.com>")
|
|
EmailAddress::parse_with_options(s, Options::default().without_display_text())
|
|
.map_err(|_| EmailError::InvalidFormat)?;
|
|
|
|
Ok(Self(s.to_owned()))
|
|
}
|
|
|
|
pub fn as_str(&self) -> &str {
|
|
&self.0
|
|
}
|
|
|
|
pub fn as_bytes(&self) -> &[u8] {
|
|
self.0.as_bytes()
|
|
}
|
|
|
|
pub fn into_inner(self) -> String {
|
|
self.0
|
|
}
|
|
|
|
pub fn into_bytes(self) -> Vec<u8> {
|
|
self.0.into_bytes()
|
|
}
|
|
|
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, EmailError> {
|
|
let s = std::str::from_utf8(bytes).map_err(|_| EmailError::InvalidUtf8)?;
|
|
Self::new(s)
|
|
}
|
|
}
|
|
|
|
/* ---- std traits ---- */
|
|
|
|
impl fmt::Display for Email {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.write_str(self.as_str())
|
|
}
|
|
}
|
|
|
|
impl FromStr for Email {
|
|
type Err = EmailError;
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Email::new(s)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<String> for Email {
|
|
type Error = EmailError;
|
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
Email::new(value)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&str> for Email {
|
|
type Error = EmailError;
|
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
Email::new(value)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&[u8]> for Email {
|
|
type Error = EmailError;
|
|
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
|
Email::from_bytes(value)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<Vec<u8>> for Email {
|
|
type Error = EmailError;
|
|
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
|
Email::from_bytes(&value)
|
|
}
|
|
}
|
|
|
|
impl AsRef<str> for Email {
|
|
fn as_ref(&self) -> &str {
|
|
self.as_str()
|
|
}
|
|
}
|
|
|
|
impl AsRef<[u8]> for Email {
|
|
fn as_ref(&self) -> &[u8] {
|
|
self.as_bytes()
|
|
}
|
|
}
|
|
|
|
impl From<Email> for Vec<u8> {
|
|
fn from(value: Email) -> Self {
|
|
value.into_bytes()
|
|
}
|
|
}
|