use async_trait::async_trait;
use chrono::NaiveDateTime;
use eyre::Result;
use serde::{Deserialize, Serialize};
use sqlx::sqlite::{SqlitePool, SqliteRow};
use sqlx::{FromRow, Row};
#[derive(Serialize, Deserialize)]
pub struct EventRequest {
pub name: String,
pub description: String,
pub date: String,
pub icon_name: Option<String>,
pub sound_file_path: Option<String>,
}
#[derive(Serialize, FromRow)]
pub struct Event {
pub id: i64,
pub name: String,
pub description: Option<String>,
pub date: String,
pub icon_name: Option<String>,
pub sound_file_path: Option<String>,
}
#[derive(Serialize, Deserialize)]
pub struct CronEventRequest {
pub name: String,
pub description: String,
pub cron_string: String,
pub icon_name: Option<String>,
pub sound_file_path: Option<String>,
}
#[derive(Serialize, FromRow)]
pub struct CronEvent {
pub id: i64,
pub name: String,
pub description: Option<String>,
pub cron_string: String,
pub icon_name: Option<String>,
pub sound_file_path: Option<String>,
pub last_notification: Option<String>,
}
async fn get_max_date(pool: &SqlitePool) -> Result<String> {
let max_date = sqlx::query("SELECT datetime(MAX(event_date), '+1 hour') AS date FROM events")
.fetch_one(&*pool)
.await?;
Ok(max_date.get(0))
}
#[async_trait]
pub trait EventRepo<T, U> {
async fn get_event(pool: &SqlitePool, id: i64) -> Result<U>;
async fn create_event(pool: &SqlitePool, request: T) -> Result<U>;
async fn delete_event(pool: &SqlitePool, id: i64) -> Result<()>;
async fn create_table(pool: &SqlitePool) -> Result<()>;
}
#[async_trait]
impl EventRepo<EventRequest, Event> for Event {
async fn get_event(pool: &SqlitePool, id: i64) -> Result<Event> {
let response = sqlx::query("SELECT * FROM events WHERE id = ?")
.bind(id)
.map(|row: SqliteRow| Event {
id: row.get(0),
name: row.get(1),
description: row.get(2),
date: row.get(3),
icon_name: row.get(4),
sound_file_path: row.get(5),
})
.fetch_one(&*pool)
.await?;
Ok(response)
}
async fn create_event(pool: &SqlitePool, request: EventRequest) -> Result<Event> {
let mut transmitter = pool.begin().await?;
let event = sqlx::query(
"
INSERT INTO events
(name, description, event_date, icon, sound_file_path)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, name, description, event_date, icon, sound_file_path
",
)
.bind(&request.name)
.bind(&request.description)
.bind(&request.date)
.bind(&request.icon_name)
.bind(&request.sound_file_path)
.map(|row: SqliteRow| Event {
id: row.get(0),
name: row.get(1),
description: row.get(2),
date: row.get(3),
icon_name: row.get(4),
sound_file_path: row.get(5),
})
.fetch_one(&mut transmitter)
.await?;
transmitter.commit().await?;
Ok(event)
}
async fn delete_event(pool: &SqlitePool, id: i64) -> Result<()> {
sqlx::query("DELETE FROM events WHERE id = $1")
.bind(id)
.execute(pool)
.await?;
Ok(())
}
async fn create_table(pool: &SqlitePool) -> Result<()> {
sqlx::query(
"CREATE TABLE IF NOT EXISTS events(
id INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
description TEXT NULL,
event_date TEXT NOT NULL,
icon TEXT NULL,
sound_file_path TEXT NULL
)
",
)
.execute(pool)
.await?;
Ok(())
}
}
impl Event {
pub async fn find_current_events(pool: &SqlitePool, date: String) -> Result<Vec<Event>> {
let response =
sqlx::query("SELECT * FROM events WHERE event_date <= ? ORDER BY event_date")
.bind(date)
.map(|row: SqliteRow| Event {
id: row.get(0),
name: row.get(1),
description: row.get(2),
date: row.get(3),
icon_name: row.get(4),
sound_file_path: row.get(5),
})
.fetch_all(&*pool)
.await?;
Ok(response)
}
pub async fn find_future_events(
pool: &SqlitePool,
date: String,
end_date: Option<String>,
) -> Result<Vec<Event>> {
let range_end: String;
if let Some(option_date) = end_date {
range_end = option_date;
} else {
range_end = get_max_date(pool).await?;
}
let response = sqlx::query(
"SELECT * FROM events WHERE event_date BETWEEN $1 AND $2 ORDER BY event_date",
)
.bind(date)
.bind(range_end)
.map(|row: SqliteRow| Event {
id: row.get(0),
name: row.get(1),
description: row.get(2),
date: row.get(3),
icon_name: row.get(4),
sound_file_path: row.get(5),
})
.fetch_all(&*pool)
.await?;
Ok(response)
}
}
#[async_trait]
impl EventRepo<CronEventRequest, CronEvent> for CronEvent {
async fn get_event(pool: &SqlitePool, id: i64) -> Result<CronEvent> {
let response = sqlx::query("SELECT * FROM recurring_events WHERE id = ?")
.bind(id)
.map(|row: SqliteRow| CronEvent {
id: row.get(0),
name: row.get(1),
description: row.get(2),
cron_string: row.get(3),
icon_name: row.get(4),
sound_file_path: row.get(5),
last_notification: row.get(6),
})
.fetch_one(&*pool)
.await?;
Ok(response)
}
async fn create_event(pool: &SqlitePool, request: CronEventRequest) -> Result<CronEvent> {
let mut transmitter = pool.begin().await?;
let event = sqlx::query(
"
INSERT INTO recurring_events
(name, description, cron_string, icon, sound_file_path)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, name, description, cron_string, icon, sound_file_path
",
)
.bind(&request.name)
.bind(&request.description)
.bind(&request.cron_string)
.bind(&request.icon_name)
.bind(&request.sound_file_path)
.map(|row: SqliteRow| CronEvent {
id: row.get(0),
name: row.get(1),
description: row.get(2),
cron_string: row.get(3),
icon_name: row.get(4),
sound_file_path: row.get(5),
last_notification: None,
})
.fetch_one(&mut transmitter)
.await?;
transmitter.commit().await?;
Ok(event)
}
async fn delete_event(pool: &SqlitePool, id: i64) -> Result<()> {
sqlx::query("DELETE FROM recurring_events WHERE id = $1")
.bind(id)
.execute(pool)
.await?;
Ok(())
}
async fn create_table(pool: &SqlitePool) -> Result<()> {
sqlx::query(
"CREATE TABLE IF NOT EXISTS recurring_events(
id INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
description TEXT NULL,
cron_string TEXT NOT NULL,
icon TEXT NULL,
sound_file_path TEXT NULL,
last_notification TEXT NULL
)
",
)
.execute(pool)
.await?;
Ok(())
}
}
impl CronEvent {
pub async fn find_events(pool: &SqlitePool) -> Result<Vec<CronEvent>> {
let response = sqlx::query("SELECT * FROM recurring_events")
.map(|row: SqliteRow| CronEvent {
id: row.get(0),
name: row.get(1),
description: row.get(2),
cron_string: row.get(3),
icon_name: row.get(4),
sound_file_path: row.get(5),
last_notification: row.get(6),
})
.fetch_all(&*pool)
.await?;
Ok(response)
}
pub async fn update_last_notification_date(
pool: &SqlitePool,
id: i64,
new_date: NaiveDateTime,
) -> Result<()> {
sqlx::query("UPDATE recurring_events SET last_notification = $1 WHERE id = $2")
.bind(new_date)
.bind(id)
.execute(pool)
.await?;
Ok(())
}
}