use crate::db::db::Db;
use anyhow::Result;
use chrono::{NaiveDate, NaiveDateTime};
use rusqlite::Connection;
const SCHEMA_SERVER_OUTBOX: &str = "CREATE TABLE IF NOT EXISTS server_outbox (
id INTEGER PRIMARY KEY,
date DATE NOT NULL UNIQUE,
queued_at TIMESTAMP NOT NULL,
attempts INTEGER NOT NULL DEFAULT 0,
last_attempt_at TIMESTAMP,
last_error TEXT
);";
const ENQUEUE: &str = "INSERT INTO server_outbox (date, queued_at, attempts, last_attempt_at, last_error)
VALUES (?1, datetime(CURRENT_TIMESTAMP, 'localtime'), 1, datetime(CURRENT_TIMESTAMP, 'localtime'), ?2)
ON CONFLICT(date) DO UPDATE SET
attempts = attempts + 1,
last_attempt_at = datetime(CURRENT_TIMESTAMP, 'localtime'),
last_error = ?2";
const SELECT_PENDING: &str = "SELECT id, date, queued_at, attempts, last_attempt_at, last_error FROM server_outbox ORDER BY date ASC";
const DELETE_BY_DATE: &str = "DELETE FROM server_outbox WHERE date = ?1";
const COUNT_PENDING: &str = "SELECT COUNT(*) FROM server_outbox";
#[derive(Debug, Clone)]
pub struct OwedDay {
pub id: i32,
pub date: NaiveDate,
pub queued_at: NaiveDateTime,
pub attempts: i32,
pub last_attempt_at: Option<NaiveDateTime>,
pub last_error: Option<String>,
}
pub struct ServerOutbox {
pub conn: Connection,
}
impl ServerOutbox {
pub fn new() -> Result<Self> {
let db = Db::new()?;
db.conn.execute(SCHEMA_SERVER_OUTBOX, [])?;
Ok(ServerOutbox { conn: db.conn })
}
pub fn enqueue(&mut self, date: NaiveDate, error: &str) -> Result<()> {
self.conn.execute(ENQUEUE, rusqlite::params![date, error])?;
Ok(())
}
pub fn pending(&self) -> Result<Vec<OwedDay>> {
let mut statement = self.conn.prepare(SELECT_PENDING)?;
let rows = statement.query_map([], |row| {
Ok(OwedDay {
id: row.get(0)?,
date: row.get(1)?,
queued_at: row.get(2)?,
attempts: row.get(3)?,
last_attempt_at: row.get(4)?,
last_error: row.get(5)?,
})
})?;
rows.collect::<rusqlite::Result<Vec<_>>>().map_err(Into::into)
}
pub fn count(&self) -> Result<i64> {
Ok(self.conn.query_row(COUNT_PENDING, [], |row| row.get(0))?)
}
pub fn remove(&mut self, date: NaiveDate) -> Result<bool> {
Ok(self.conn.execute(DELETE_BY_DATE, rusqlite::params![date])? > 0)
}
}