use std::str::FromStr;
use snafu::{ResultExt as _, Snafu};
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Hash,
derive_more::AsRef,
derive_more::Display,
derive_more::From,
derive_more::Into,
serde::Serialize,
serde::Deserialize,
)]
pub struct EmailAddress(email_address::EmailAddress);
impl EmailAddress {
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
#[derive(Debug, Snafu)]
pub enum TryFromEmailAddressError {
#[snafu(display("Failed to parse e-mail address: {source}"))]
Parse { source: email_address::Error },
}
impl FromStr for EmailAddress {
type Err = TryFromEmailAddressError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let email_address: email_address::EmailAddress = s.parse().context(ParseSnafu)?;
Ok(Self(email_address))
}
}
impl PartialOrd for EmailAddress {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for EmailAddress {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
impl TryFrom<String> for EmailAddress {
type Error = TryFromEmailAddressError;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}
#[cfg(feature = "sea-orm")]
mod sea_orm_impls {
use sea_orm::{
ActiveValue, ColumnType, DbErr, IntoActiveValue, TryGetError, TryGetable, Value,
sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr},
};
use super::EmailAddress;
impl From<EmailAddress> for Value {
fn from(value: EmailAddress) -> Self {
value.as_str().into()
}
}
impl TryGetable for EmailAddress {
fn try_get_by<I: sea_orm::ColIdx>(
res: &sea_orm::QueryResult,
index: I,
) -> Result<Self, TryGetError> {
let s = <String as TryGetable>::try_get_by(res, index)?;
s.try_into().map_err(|e| {
DbErr::TryIntoErr {
from: "String",
into: "EmailAddress",
source: Box::new(e),
}
.into()
})
}
}
impl ValueType for EmailAddress {
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
let s = <String as ValueType>::try_from(v)?;
s.try_into().map_err(|_| ValueTypeErr)
}
fn type_name() -> String {
stringify!(String).to_owned()
}
fn array_type() -> ArrayType {
<String as ValueType>::array_type()
}
fn column_type() -> ColumnType {
<String as ValueType>::column_type()
}
}
impl Nullable for EmailAddress {
fn null() -> Value {
Value::String(None)
}
}
impl IntoActiveValue<EmailAddress> for EmailAddress {
fn into_active_value(self) -> ActiveValue<EmailAddress> {
ActiveValue::Set(self)
}
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::EmailAddress;
#[test]
fn from_str() {
assert_eq!(
EmailAddress(
"hello@example.com"
.parse()
.expect("value must be a parseable EmailAddress")
),
"hello@example.com"
.parse()
.expect("value must be a parseable EmailAddress")
);
}
}
#[cfg(test)]
mod serde_tests {
use pretty_assertions::assert_eq;
use serde_json::json;
use super::EmailAddress;
#[test]
fn serialize() {
assert_eq!(
json!(EmailAddress(
"hello@example.com"
.parse()
.expect("value must be a parseable EmailAddress")
)),
json!("hello@example.com")
);
}
#[test]
fn deserialize_good() {
let deserialized: EmailAddress = serde_json::from_value(json!("hello@example.com"))
.expect("value must be deserializable as EmailAddress");
assert_eq!(
deserialized,
EmailAddress(
"hello@example.com"
.parse()
.expect("value must be a parseable EmailAddress")
)
);
}
#[test]
fn deserialize_bad() {
assert!(serde_json::from_value::<EmailAddress>(json!("xyzabcd")).is_err());
assert!(serde_json::from_value::<EmailAddress>(json!(true)).is_err());
assert!(serde_json::from_value::<EmailAddress>(json!(123)).is_err());
}
}