Files
zero2prod/src/routes/subscriptions.rs
2025-08-23 11:13:57 +02:00

77 lines
1.9 KiB
Rust

use crate::domain::{NewSubscriber, SubscriberEmail, SubscriberName};
use axum::{Form, extract::State, http::StatusCode, response::IntoResponse};
use chrono::Utc;
use serde::Deserialize;
use sqlx::PgPool;
use uuid::Uuid;
#[tracing::instrument(
name = "Adding a new subscriber",
skip(connection, form),
fields(
subscriber_email = %form.email,
subscriber_name = %form.name
)
)]
pub async fn subscribe(
State(connection): State<PgPool>,
Form(form): Form<FormData>,
) -> impl IntoResponse {
let new_subscriber = match form.try_into() {
Ok(subscriber) => subscriber,
Err(_) => return StatusCode::BAD_REQUEST,
};
if insert_subscriber(&connection, &new_subscriber)
.await
.is_err()
{
StatusCode::INTERNAL_SERVER_ERROR
} else {
StatusCode::OK
}
}
#[tracing::instrument(
name = "Saving new subscriber details in the database",
skip(connection, new_subscriber)
)]
pub async fn insert_subscriber(
connection: &PgPool,
new_subscriber: &NewSubscriber,
) -> Result<(), sqlx::Error> {
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()
)
.execute(connection)
.await
.map_err(|e| {
tracing::error!("Failed to execute query: {:?}", e);
e
})?;
Ok(())
}
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
pub struct FormData {
name: String,
email: String,
}
impl TryFrom<FormData> for NewSubscriber {
type Error = String;
fn try_from(value: FormData) -> Result<Self, Self::Error> {
let name = SubscriberName::parse(value.name)?;
let email = SubscriberEmail::parse(value.email)?;
Ok(Self { name, email })
}
}