diff --git a/src/email_address/mod.rs b/src/email_address/mod.rs new file mode 100644 index 0000000..db1f77d --- /dev/null +++ b/src/email_address/mod.rs @@ -0,0 +1,35 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. + +//! Email address type. + +use serde::de::{Deserialize, Deserializer, Error, Unexpected}; + +use validate; + +#[cfg(test)] +mod test; + +#[derive(Clone, Debug, Default, Serialize, PartialEq)] +pub struct EmailAddress(pub String); + +/// Email address type. +/// +/// Validates and then lowercases the address during deserialization. +impl<'d> Deserialize<'d> for EmailAddress { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'d>, + { + let value: String = Deserialize::deserialize(deserializer)?; + if validate::email_address(&value) { + Ok(EmailAddress(value.to_lowercase())) + } else { + Err(D::Error::invalid_value( + Unexpected::Str(&value), + &"email address", + )) + } + } +} diff --git a/src/email_address/test.rs b/src/email_address/test.rs new file mode 100644 index 0000000..de5f832 --- /dev/null +++ b/src/email_address/test.rs @@ -0,0 +1,43 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// // file, you can obtain one at https://mozilla.org/MPL/2.0/. + +use serde_test::{assert_de_tokens, Token}; + +use super::*; + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct TestEmailStruct { + email: EmailAddress, +} + +#[test] +fn always_lowercase_email() { + let expected = TestEmailStruct { + email: EmailAddress(String::from("foo@example.com")), + }; + assert_de_tokens( + &expected, + &[ + Token::Struct { + name: "TestEmailStruct", + len: 1, + }, + Token::Str("email"), + Token::Str("foo@example.com"), + Token::StructEnd, + ], + ); + assert_de_tokens( + &expected, + &[ + Token::Struct { + name: "TestEmailStruct", + len: 1, + }, + Token::Str("email"), + Token::Str("FOO@EXAMPLE.COM"), + Token::StructEnd, + ], + ); +} diff --git a/src/lib.rs b/src/lib.rs index 7c244f7..f735ee7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,7 @@ pub mod app_errors; pub mod auth_db; pub mod bounces; pub mod duration; +pub mod email_address; pub mod logging; pub mod message_data; pub mod providers; diff --git a/src/send/mod.rs b/src/send/mod.rs index deda680..2d94ebe 100644 --- a/src/send/mod.rs +++ b/src/send/mod.rs @@ -11,11 +11,11 @@ use rocket::{ Data, Outcome, Request, State, }; use rocket_contrib::{Json, Value}; -use serde::de::{Deserialize, Deserializer, Error, Unexpected}; use app_errors::{AppError, AppErrorKind, AppResult}; use auth_db::DbClient; use bounces::Bounces; +use email_address::EmailAddress; use message_data::MessageData; use providers::{Headers, Providers}; use validate; @@ -29,26 +29,6 @@ struct Body { html: Option, } -#[derive(Clone, Debug, Default, Serialize, PartialEq)] -pub struct EmailAddress(pub String); - -impl<'d> Deserialize<'d> for EmailAddress { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'d>, - { - let value: String = Deserialize::deserialize(deserializer)?; - if validate::email_address(&value) { - Ok(EmailAddress(value.to_lowercase())) - } else { - Err(D::Error::invalid_value( - Unexpected::Str(&value), - &"email address", - )) - } - } -} - #[derive(Debug, Deserialize)] struct Email { to: EmailAddress, diff --git a/src/send/test.rs b/src/send/test.rs index 0a9a60c..6bed125 100644 --- a/src/send/test.rs +++ b/src/send/test.rs @@ -7,9 +7,7 @@ use rocket::{ http::{ContentType, Status}, local::Client, }; -use serde_test::{assert_de_tokens, Token}; -use super::EmailAddress; use app_errors::{self, AppError, AppErrorKind}; use auth_db::DbClient; use bounces::Bounces; @@ -43,46 +41,6 @@ fn setup() -> Client { Client::new(server).unwrap() } -#[derive(Serialize, Deserialize, PartialEq, Debug)] -struct TestEmailStruct { - email: EmailAddress, -} - -#[test] -fn always_lowercase_email() { - let lowercase = TestEmailStruct { - email: EmailAddress(String::from("foo@example.com")), - }; - assert_de_tokens( - &lowercase, - &[ - Token::Struct { - name: "TestEmailStruct", - len: 1, - }, - Token::Str("email"), - Token::Str("foo@example.com"), - Token::StructEnd, - ], - ); - - let uppercase = TestEmailStruct { - email: EmailAddress(String::from("foo@example.com")), - }; - assert_de_tokens( - &uppercase, - &[ - Token::Struct { - name: "TestEmailStruct", - len: 1, - }, - Token::Str("email"), - Token::Str("FOO@EXAMPLE.COM"), - Token::StructEnd, - ], - ); -} - #[test] fn single_recipient() { let client = setup(); diff --git a/src/settings/mod.rs b/src/settings/mod.rs index c13e8f8..f064173 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -13,8 +13,8 @@ use config::{Config, ConfigError, Environment, File}; use serde::de::{Deserialize, Deserializer, Error, Unexpected}; use duration::Duration; +use email_address::EmailAddress; use logging::MozlogLogger; -use send::EmailAddress; use serialize; use validate;