use crate::errors::ZTPError; use crate::session_id::SessionId; use axum::{http::StatusCode, Extension, Form}; use chrono::{DateTime, Utc}; use sqlx::types::Uuid; use sqlx::PgPool; #[derive(serde::Deserialize)] pub(crate) struct NewSubscription { pub email: String, pub name: String, } #[derive(serde::Deserialize, serde::Serialize, sqlx::FromRow)] struct Subscription { pub id: Uuid, pub email: String, pub name: String, pub subscribed_at: DateTime, } impl From<&NewSubscription> for Subscription { fn from(s: &NewSubscription) -> Self { Subscription { id: Uuid::new_v4(), email: s.email.clone(), name: s.name.clone(), subscribed_at: Utc::now(), } } } pub(crate) async fn subscribe( Extension(session): Extension, Extension(pool): Extension, payload: Option>, ) -> Result<(StatusCode, ()), ZTPError> { if let Some(payload) = payload { let sql = "INSERT INTO subscriptions (id, email, name, subscribed_at) VALUES ($1, $2, $3, $4);".to_string(); let subscription: Subscription = (&(payload.0)).into(); tracing::info!( "request_id {} - Adding '{}' as a new subscriber.", session.0.to_string(), subscription.name ); sqlx::query(&sql) .bind(subscription.id) .bind(subscription.email) .bind(subscription.name) .bind(subscription.subscribed_at) .execute(&pool) .await .map_or(Err(ZTPError::DuplicateEmail), |_| Ok((StatusCode::OK, ()))) } else { Err(ZTPError::FormIncomplete) } }