Use TryInto.

This commit is contained in:
LukeMathWalker 2020-12-29 10:00:11 +00:00
parent f6d62fb7a1
commit 9dd9aaffd6
6 changed files with 89 additions and 12 deletions

45
Cargo.lock generated
View file

@ -701,6 +701,25 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"log",
"regex",
]
[[package]]
name = "fake"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6479fa2c7e83ddf8be7d435421e093b072ca891b99a49bc84eba098f4044f818"
dependencies = [
"rand",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.19" version = "1.0.19"
@ -1609,6 +1628,29 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quickcheck"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f"
dependencies = [
"env_logger",
"log",
"rand",
"rand_core",
]
[[package]]
name = "quickcheck_macros"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.7" version = "1.0.7"
@ -2921,7 +2963,10 @@ dependencies = [
"chrono", "chrono",
"claim", "claim",
"config", "config",
"fake",
"lazy_static", "lazy_static",
"quickcheck",
"quickcheck_macros",
"reqwest", "reqwest",
"serde", "serde",
"serde-aux", "serde-aux",

View file

@ -30,8 +30,11 @@ tracing-actix-web = "0.2.0"
serde-aux = "1.0.1" serde-aux = "1.0.1"
unicode-segmentation = "1.7.1" unicode-segmentation = "1.7.1"
validator = "0.12.0" validator = "0.12.0"
quickcheck_macros = "0.9.1"
[dev-dependencies] [dev-dependencies]
reqwest = { version = "0.10.7", features = ["json"] } reqwest = { version = "0.10.7", features = ["json"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
claim = "0.4.0" claim = "0.4.0"
quickcheck = "0.9.2"
fake = "2.3.0"

View file

@ -1,6 +1,7 @@
mod subscriber_name;
mod subscriber_email;
mod new_subscriber; mod new_subscriber;
mod subscriber_email;
mod subscriber_name;
pub use subscriber_name::SubscriberName;
pub use new_subscriber::NewSubscriber; pub use new_subscriber::NewSubscriber;
pub use subscriber_email::SubscriberEmail;
pub use subscriber_name::SubscriberName;

View file

@ -1,6 +1,8 @@
use crate::domain::subscriber_name::SubscriberName; use crate::domain::subscriber_name::SubscriberName;
use crate::domain::subscriber_email::SubscriberEmail;
pub struct NewSubscriber { pub struct NewSubscriber {
pub email: String, // We are not using `String` anymore!
pub email: SubscriberEmail,
pub name: SubscriberName, pub name: SubscriberName,
} }

View file

@ -23,6 +23,8 @@ impl AsRef<str> for SubscriberEmail {
mod tests { mod tests {
use super::SubscriberEmail; use super::SubscriberEmail;
use claim::assert_err; use claim::assert_err;
use fake::Fake;
use fake::faker::internet::en::SafeEmail;
#[test] #[test]
fn empty_string_is_rejected() { fn empty_string_is_rejected() {
@ -41,4 +43,19 @@ mod tests {
let email = "@domain.com".to_string(); let email = "@domain.com".to_string();
assert_err!(SubscriberEmail::parse(email)); assert_err!(SubscriberEmail::parse(email));
} }
#[derive(Debug, Clone)]
struct ValidEmailFixture(pub String);
impl quickcheck::Arbitrary for ValidEmailFixture {
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
let email = SafeEmail().fake_with_rng(g);
Self(email)
}
}
#[quickcheck_macros::quickcheck]
fn valid_emails_are_parsed_successfully(valid_email: ValidEmailFixture) -> bool {
SubscriberEmail::parse(valid_email.0).is_ok()
}
} }

View file

@ -1,7 +1,8 @@
use crate::domain::{NewSubscriber, SubscriberName}; use crate::domain::{NewSubscriber, SubscriberEmail, SubscriberName};
use actix_web::{web, HttpResponse}; use actix_web::{web, HttpResponse};
use chrono::Utc; use chrono::Utc;
use sqlx::PgPool; use sqlx::PgPool;
use std::convert::TryInto;
use uuid::Uuid; use uuid::Uuid;
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
@ -10,6 +11,16 @@ pub struct FormData {
name: String, name: String,
} }
impl TryInto<NewSubscriber> for FormData {
type Error = String;
fn try_into(self) -> Result<NewSubscriber, Self::Error> {
let name = SubscriberName::parse(self.name)?;
let email = SubscriberEmail::parse(self.email)?;
Ok(NewSubscriber { email, name })
}
}
#[tracing::instrument( #[tracing::instrument(
name = "Adding a new subscriber", name = "Adding a new subscriber",
skip(form, pool), skip(form, pool),
@ -22,12 +33,10 @@ pub async fn subscribe(
form: web::Form<FormData>, form: web::Form<FormData>,
pool: web::Data<PgPool>, pool: web::Data<PgPool>,
) -> Result<HttpResponse, HttpResponse> { ) -> Result<HttpResponse, HttpResponse> {
let name = let new_subscriber = form
SubscriberName::parse(form.0.name).map_err(|_| HttpResponse::BadRequest().finish())?; .0
let new_subscriber = NewSubscriber { .try_into()
email: form.0.email, .map_err(|_| HttpResponse::BadRequest().finish())?;
name,
};
insert_subscriber(&pool, &new_subscriber) insert_subscriber(&pool, &new_subscriber)
.await .await
.map_err(|_| HttpResponse::InternalServerError().finish())?; .map_err(|_| HttpResponse::InternalServerError().finish())?;
@ -48,7 +57,7 @@ pub async fn insert_subscriber(
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4)
"#, "#,
Uuid::new_v4(), Uuid::new_v4(),
new_subscriber.email, new_subscriber.email.as_ref(),
new_subscriber.name.as_ref(), new_subscriber.name.as_ref(),
Utc::now() Utc::now()
) )