use crate::models::{Todo, TodoList};
use sqlx::SqlitePool;
impl TodoList {
pub async fn sync_to_db(&self, pool: &SqlitePool) -> color_eyre::Result<()> {
let mut tx = pool.begin().await?;
let in_memory_ids: Vec<String> = self.todos.keys().cloned().collect();
if in_memory_ids.is_empty() {
sqlx::query("DELETE FROM todos")
.execute(&mut *tx)
.await?;
} else {
let placeholders = in_memory_ids.iter().map(|_| "?").collect::<Vec<_>>().join(",");
let delete_sql = format!(
"DELETE FROM todos WHERE id NOT IN ({})",
placeholders
);
let mut query = sqlx::query(&delete_sql);
for id in &in_memory_ids {
query = query.bind(id);
}
query.execute(&mut *tx).await?;
}
for todo in self.todos.values() {
sqlx::query(
"INSERT INTO todos (id, content, created_at, finished_at, finished)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
content = excluded.content,
finished_at = excluded.finished_at,
finished = excluded.finished",
)
.bind(todo.id)
.bind(&todo.content)
.bind(todo.created_at)
.bind(todo.finished_at)
.bind(todo.finished)
.execute(&mut *tx)
.await?;
}
tx.commit().await?;
Ok(())
}
pub async fn load_from_db(&mut self, pool: &SqlitePool) -> color_eyre::Result<()> {
let rows = sqlx::query_as::<_, Todo>("SELECT * FROM todos")
.fetch_all(pool)
.await?;
self.todos = rows.into_iter().map(|t| (t.id.to_string(), t)).collect();
Ok(())
}
pub async fn find_by_content(
pool: &SqlitePool,
content: &str,
) -> color_eyre::Result<Option<Todo>> {
let res = sqlx::query_as::<_, Todo>("SELECT * FROM todos WHERE content = ? LIMIT 1")
.bind(content)
.fetch_optional(pool)
.await?;
Ok(res)
}
}