Skip to main content

puerto_sqlx/
mail.rs

1//! The sent-mail log (ADR-0020): which mails a worker consumer already sent for which event. Its
2//! table is [`crate::SENT_MAILS_MIGRATION`], which the first `puerto generate mail --on` writes
3//! into the project.
4
5use async_trait::async_trait;
6use puerto_core::RepositoryError;
7use puerto_core::mail::SentMails;
8use sqlx::PgPool;
9use uuid::Uuid;
10
11use crate::map_sqlx_error;
12
13/// `sent_mails`. Marking the same mail for the same event twice keeps one row.
14pub struct PgSentMails {
15    pool: PgPool,
16}
17
18impl PgSentMails {
19    pub fn new(pool: PgPool) -> Self {
20        Self { pool }
21    }
22}
23
24#[async_trait]
25impl SentMails for PgSentMails {
26    async fn was_sent(&self, event_id: Uuid, mail: &str) -> Result<bool, RepositoryError> {
27        sqlx::query_scalar(
28            "SELECT EXISTS (SELECT 1 FROM sent_mails WHERE event_id = $1 AND mail = $2)",
29        )
30        .bind(event_id)
31        .bind(mail)
32        .fetch_one(&self.pool)
33        .await
34        .map_err(|err| {
35            tracing::error!(error = %err, %event_id, mail, "sent_mails: read failed");
36            map_sqlx_error(&err)
37        })
38    }
39
40    async fn mark_sent(&self, event_id: Uuid, mail: &str) -> Result<(), RepositoryError> {
41        sqlx::query(
42            "INSERT INTO sent_mails (event_id, mail) VALUES ($1, $2) \
43             ON CONFLICT (event_id, mail) DO NOTHING",
44        )
45        .bind(event_id)
46        .bind(mail)
47        .execute(&self.pool)
48        .await
49        .map(|_| ())
50        .map_err(|err| {
51            tracing::error!(error = %err, %event_id, mail, "sent_mails: mark failed");
52            map_sqlx_error(&err)
53        })
54    }
55}