use crate::ports::{Database, DbError, Statement};
use sea_query::{Alias, Expr, OnConflict, Query};
fn iden(name: &str) -> Alias {
Alias::new(name)
}
#[derive(Debug, Clone)]
pub struct SendCooldown {
table: String,
}
impl SendCooldown {
#[must_use]
pub fn new(table: impl Into<String>) -> Self {
Self {
table: table.into(),
}
}
#[must_use]
pub fn create_table_sql(&self) -> String {
format!(
"CREATE TABLE IF NOT EXISTS {table} (\n \
subject TEXT PRIMARY KEY,\n \
last_sent_at TEXT NOT NULL\n);",
table = self.table
)
}
pub async fn try_acquire(
&self,
db: &dyn Database,
subject: &str,
now: &str,
cutoff: &str,
) -> Result<bool, DbError> {
let mut renew = Query::update();
renew
.table(iden(&self.table))
.value(iden("last_sent_at"), now)
.and_where(Expr::col(iden("subject")).eq(subject))
.and_where(Expr::col(iden("last_sent_at")).lt(cutoff));
if db.execute(&Statement::render(&renew)).await? == 1 {
return Ok(true);
}
let mut first = Query::insert();
first
.into_table(iden(&self.table))
.columns(["subject", "last_sent_at"])
.values_panic([subject.to_owned().into(), now.to_owned().into()])
.on_conflict(OnConflict::column(iden("subject")).do_nothing().to_owned());
Ok(db.execute(&Statement::render(&first)).await? == 1)
}
pub async fn release(&self, db: &dyn Database, subject: &str) -> Result<(), DbError> {
let mut delete = Query::delete();
delete
.from_table(iden(&self.table))
.and_where(Expr::col(iden("subject")).eq(subject));
db.execute(&Statement::render(&delete)).await?;
Ok(())
}
pub async fn prune(&self, db: &dyn Database, before: &str) -> Result<u64, DbError> {
let mut delete = Query::delete();
delete
.from_table(iden(&self.table))
.and_where(Expr::col(iden("last_sent_at")).lt(before));
db.execute(&Statement::render(&delete)).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_table_sql_is_portable_ddl() {
let sql = SendCooldown::new("waitlist_send_cooldown").create_table_sql();
assert!(sql.contains("CREATE TABLE IF NOT EXISTS waitlist_send_cooldown"));
assert!(sql.contains("subject TEXT PRIMARY KEY"));
assert!(sql.contains("last_sent_at TEXT NOT NULL"));
}
}