From f6d62fb7a1802c43c41d43fcf075b938c43dce8d Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Mon, 28 Dec 2020 12:26:05 +0000 Subject: [PATCH] Add email validation. --- Cargo.lock | 23 ++++++++++ Cargo.toml | 1 + src/domain/mod.rs | 6 +++ src/domain/new_subscriber.rs | 6 +++ src/domain/subscriber_email.rs | 44 ++++++++++++++++++++ src/{domain.rs => domain/subscriber_name.rs} | 7 +--- 6 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 src/domain/mod.rs create mode 100644 src/domain/new_subscriber.rs create mode 100644 src/domain/subscriber_email.rs rename src/{domain.rs => domain/subscriber_name.rs} (95%) diff --git a/Cargo.lock b/Cargo.lock index 1e66c55..b76539f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2646,6 +2646,28 @@ dependencies = [ "rand", ] +[[package]] +name = "validator" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d6937c33ec6039d8071bcf72933146b5bbe378d645d8fa59bdadabfc2a249" +dependencies = [ + "idna", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_types", +] + +[[package]] +name = "validator_types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" + [[package]] name = "vcpkg" version = "0.2.10" @@ -2913,4 +2935,5 @@ dependencies = [ "tracing-subscriber", "unicode-segmentation", "uuid", + "validator", ] diff --git a/Cargo.toml b/Cargo.toml index 6fbf417..fe8f6bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ tracing-log = "0.1.1" tracing-actix-web = "0.2.0" serde-aux = "1.0.1" unicode-segmentation = "1.7.1" +validator = "0.12.0" [dev-dependencies] reqwest = { version = "0.10.7", features = ["json"] } diff --git a/src/domain/mod.rs b/src/domain/mod.rs new file mode 100644 index 0000000..d59765a --- /dev/null +++ b/src/domain/mod.rs @@ -0,0 +1,6 @@ +mod subscriber_name; +mod subscriber_email; +mod new_subscriber; + +pub use subscriber_name::SubscriberName; +pub use new_subscriber::NewSubscriber; diff --git a/src/domain/new_subscriber.rs b/src/domain/new_subscriber.rs new file mode 100644 index 0000000..d422b03 --- /dev/null +++ b/src/domain/new_subscriber.rs @@ -0,0 +1,6 @@ +use crate::domain::subscriber_name::SubscriberName; + +pub struct NewSubscriber { + pub email: String, + pub name: SubscriberName, +} diff --git a/src/domain/subscriber_email.rs b/src/domain/subscriber_email.rs new file mode 100644 index 0000000..91f30b9 --- /dev/null +++ b/src/domain/subscriber_email.rs @@ -0,0 +1,44 @@ +use validator::validate_email; + +#[derive(Debug)] +pub struct SubscriberEmail(String); + +impl SubscriberEmail { + pub fn parse(s: String) -> Result { + if validate_email(&s) { + Ok(Self(s)) + } else { + Err(format!("{} is not a valid subscriber email.", s)) + } + } +} + +impl AsRef for SubscriberEmail { + fn as_ref(&self) -> &str { + &self.0 + } +} + +#[cfg(test)] +mod tests { + use super::SubscriberEmail; + use claim::assert_err; + + #[test] + fn empty_string_is_rejected() { + let email = "".to_string(); + assert_err!(SubscriberEmail::parse(email)); + } + + #[test] + fn email_missing_at_symbol_is_rejected() { + let email = "ursuladomain.com".to_string(); + assert_err!(SubscriberEmail::parse(email)); + } + + #[test] + fn email_missing_subject_is_rejected() { + let email = "@domain.com".to_string(); + assert_err!(SubscriberEmail::parse(email)); + } +} \ No newline at end of file diff --git a/src/domain.rs b/src/domain/subscriber_name.rs similarity index 95% rename from src/domain.rs rename to src/domain/subscriber_name.rs index 2f52375..b3c6540 100644 --- a/src/domain.rs +++ b/src/domain/subscriber_name.rs @@ -1,16 +1,11 @@ use unicode_segmentation::UnicodeSegmentation; -pub struct NewSubscriber { - pub email: String, - pub name: SubscriberName, -} - #[derive(Debug)] pub struct SubscriberName(String); impl SubscriberName { /// Returns an instance of `SubscriberName` if the input satisfies all - /// our validation constraints on subscriber names. + /// our validation constraints on subscriber names. /// It panics otherwise. pub fn parse(s: String) -> Result { // `.trim()` returns a view over the input `s` without trailing