use crate::domain::SubscriberEmail; use crate::domain::{NewSubscriber, SubscriberName}; use axum::extract::State; use axum::routing::post; use axum::Form; use axum::Router; use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; use chrono::Utc; use serde::Deserialize; use sqlx::PgPool; use uuid::Uuid; #[derive(Debug, Deserialize)] struct FormData { email: String, name: String, } #[tracing::instrument( name = "Adding a new subscriber", skip(form, pool), fields( request_id = %Uuid::new_v4(), subscriber_email = %form.email, subscriber_name = %form.name ) )] pub async fn subscribe(State(pool): State, Form(form): Form) -> Response { let name = match SubscriberName::parse(form.name) { Ok(name) => name, Err(_) => { return (StatusCode::BAD_REQUEST, "Invalid name").into_response(); } }; let email = match SubscriberEmail::parse(form.email) { Ok(email) => email, Err(_) => { return (StatusCode::BAD_REQUEST, "Invalid email address").into_response(); } }; let new_subscriber = NewSubscriber { email, name }; match insert_subscriber(&pool, &new_subscriber).await { Ok(_) => { return (StatusCode::OK,).into_response(); } Err(_) => { return (StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong").into_response(); } } } #[tracing::instrument( name = "Saving new subscriber details in the database", skip(new_subscriber, pool) )] pub async fn insert_subscriber( pool: &PgPool, new_subscriber: &NewSubscriber, ) -> Result<(), sqlx::Error> { let _ = sqlx::query!( r#" INSERT INTO subscriptions (id, email, name, subscribed_at) VALUES ($1, $2, $3, $4) "#, Uuid::new_v4(), new_subscriber.email.as_ref(), new_subscriber.name.as_ref(), Utc::now() ) // We use `get_ref` to get an immutable reference to the `PgConnection` // wrapped by `web::Data`. .execute(pool) .await .map_err(|e| { tracing::error!("Failed to execute query: {:?}", e); e }); Ok(()) } pub fn routes_subscriptions(pool: PgPool) -> Router { Router::new() .route("/subscriptions", post(subscribe)) .with_state(pool) }