use crate::JsonType;
use crate::expect_core::Context;
use crate::expect_core::ExpectOp;
use crate::expect_core::ExpectOpError;
use crate::expect_core::ExpectOpResult;
use crate::expect_core::expect_op;
use core::str::FromStr;
use email_address::EmailAddress;
#[expect_op(internal, name = "email")]
#[derive(Debug, Clone, Default, PartialEq)]
pub struct ExpectEmail {
expected_domain: Option<String>,
expected_local_part: Option<String>,
}
impl ExpectEmail {
pub(crate) fn new() -> Self {
Self {
expected_domain: None,
expected_local_part: None,
}
}
pub fn local_part<S>(mut self, local_part: S) -> Self
where
S: Into<String>,
{
self.expected_local_part = Some(local_part.into());
self
}
pub fn domain<S>(mut self, domain: S) -> Self
where
S: Into<String>,
{
self.expected_domain = Some(domain.into());
self
}
}
impl ExpectOp for ExpectEmail {
fn on_string(&self, context: &mut Context, received: &str) -> ExpectOpResult<()> {
let email = EmailAddress::from_str(received).map_err(|e| {
let error_message = format!("Invalid email address, received '{received}'");
ExpectOpError::custom_error(self, context, error_message, e)
})?;
if let Some(expected_local_part) = &self.expected_local_part {
if email.local_part() != expected_local_part {
return Err(ExpectOpError::custom(
self,
context,
format!(
"Local part mismatch, expected '{expected_local_part}', received '{received}'"
),
));
}
}
if let Some(expected_domain) = &self.expected_domain {
if email.domain() != expected_domain {
return Err(ExpectOpError::custom(
self,
context,
format!("Domain mismatch, expected '{expected_domain}', received '{received}'"),
));
}
}
Ok(())
}
fn debug_supported_types(&self) -> &'static [JsonType] {
&[JsonType::String]
}
}
#[cfg(test)]
mod test_email {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_accept_valid_email() {
let left = json!("test@example.com");
let right = json!(expect::email());
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_accept_valid_email_with_plus_sign() {
let left = json!("test+test@example.com");
let right = json!(expect::email());
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_accept_email_inside_object() {
let left = json!({ "name": "Joe", "email": "test@example.com" });
let right = json!({
"name": "Joe",
"email": expect::email(),
});
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_reject_invalid_email() {
let left = json!("🦊");
let right = json!(expect::email());
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::email() error at root:
Invalid email address, received '🦊',
Missing separator character '@'."#
);
}
}
#[cfg(test)]
mod test_local_part {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_accept_valid_local_part() {
let left = json!("test@example.com");
let right = json!(expect::email().local_part("test"));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_reject_invalid_local_part() {
let left = json!("test@example.com");
let right = json!(expect::email().local_part("🦊"));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::email() error at root:
Local part mismatch, expected '🦊', received 'test@example.com'"#
);
}
}
#[cfg(test)]
mod test_domain {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_accept_valid_domain() {
let left = json!("test@example.com");
let right = json!(expect::email().domain("example.com"));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_reject_invalid_domain() {
let left = json!("test@example.com");
let right = json!(expect::email().domain("🦊.fox"));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::email() error at root:
Domain mismatch, expected '🦊.fox', received 'test@example.com'"#
);
}
}