use crate::DDG_DOMAIN;
use std::fmt;
use std::str;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct DuckAddress(String);
#[derive(Debug, Clone)]
pub struct ParseDuckAddressError;
impl DuckAddress {
pub fn get_alias(&self) -> &str {
&self.0
}
}
impl str::FromStr for DuckAddress {
type Err = ParseDuckAddressError;
fn from_str(s: &str) -> Result<Self, ParseDuckAddressError> {
let idx = match s.find('@') {
Some(idx) => {
if !s.ends_with(DDG_DOMAIN) {
return Err(ParseDuckAddressError);
}
idx
}
None => s.len(),
};
let s = &s[0..idx];
let is_valid = s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-');
if is_valid {
Ok(DuckAddress(s.to_string()))
} else {
Err(ParseDuckAddressError)
}
}
}
impl TryFrom<String> for DuckAddress {
type Error = ParseDuckAddressError;
fn try_from(s: String) -> Result<Self, ParseDuckAddressError> {
str::FromStr::from_str(&s)
}
}
impl fmt::Display for DuckAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let duck_email = format!("{}@{}", self.0, DDG_DOMAIN);
fmt::Display::fmt(&duck_email, f)
}
}
pub fn parse_duck_address(s: &str) -> Result<DuckAddress, String> {
DuckAddress::from_str(s).map_err(|_| format!("{s} is not a valid duck address"))
}
#[derive(Debug, Clone)]
pub struct EmailAddress {
pub local_part: String,
pub domain: String,
}
#[derive(Debug, Clone)]
pub struct ParseEmailAddressError;
impl str::FromStr for EmailAddress {
type Err = ParseEmailAddressError;
fn from_str(s: &str) -> Result<Self, ParseEmailAddressError> {
let idx = match s.find('@') {
Some(idx) => idx,
None => return Err(ParseEmailAddressError),
};
let local_part = &s[0..idx];
let domain = &s[idx + 1..s.len()];
fn is_valid_char(c: char) -> bool {
c.is_ascii_alphanumeric()
|| c == '-'
|| c == '!'
|| c == '#'
|| c == '$'
|| c == '%'
|| c == '&'
|| c == '*'
|| c == '\''
|| c == '+'
|| c == '-'
|| c == '/'
|| c == '='
|| c == '?'
|| c == '^'
|| c == '_'
|| c == '`'
|| c == '{'
|| c == '|'
|| c == '}'
|| c == '~'
|| c == '.'
}
let local_part_valid = local_part.chars().all(is_valid_char);
let domain_valid = domain.chars().all(is_valid_char);
if local_part_valid && domain_valid {
Ok(EmailAddress {
local_part: local_part.to_string(),
domain: domain.to_string(),
})
} else {
Err(ParseEmailAddressError)
}
}
}
pub fn parse_email_address(s: &str) -> Result<EmailAddress, String> {
EmailAddress::from_str(s).map_err(|_| format!("{s} is not a valid email address"))
}