use crate::Result; use anyhow::anyhow; use sqlx::{self, ConnectOptions, SqliteConnection}; use sqlx::sqlite::SqliteConnectOptions; pub(crate) type Id = i64; #[derive(sqlx::FromRow)] pub struct Bookmark { #[sqlx(rename="rowid")] pub id: Id, pub link: String, pub comment: String, } impl Bookmark { pub fn new(link: String, comment: String) -> Self { Bookmark { id: 0, link, comment } } } pub struct Store { conn: SqliteConnection, table: &'static str, } impl Store { pub async fn new(location: &str, is_reading_list: bool) -> Result { let conn = location.parse::()? .connect().await?; let table = { if is_reading_list { "reading_list" } else { "bookmarks" } }; let mut result = Self { conn, table }; result.create_tables().await?; Ok(result) } pub async fn list(&mut self) -> Result> { let query = format!( "SELECT rowid, link, comment FROM {} ORDER BY timestamp", self.table ); Ok(sqlx::query_as(&query) .fetch_all(&mut self.conn) .await?) } pub async fn add(&mut self, bookmark: Bookmark) -> Result<()> { let query = format!( "INSERT INTO {} VALUES ($1, $2, unixepoch('now'))", self.table ); sqlx::query(&query) .bind(bookmark.link) .bind(bookmark.comment) .execute(&mut self.conn).await?; Ok(()) } pub async fn get(&mut self, id: Id) -> Result { let query = format!( "SELECT rowid, link, comment FROM {} WHERE rowid = $1", self.table ); let result = sqlx::query_as(&query) .bind(id) .fetch_optional(&mut self.conn) .await? .ok_or(anyhow!("Could not find a bookmark with that ID!"))?; Ok(result) } pub async fn rm(&mut self, id: Id) -> Result<()> { let query = format!( "DELETE FROM {} WHERE rowid = $1", self.table ); sqlx::query(&query).bind(id).execute(&mut self.conn).await?; Ok(()) } async fn create_tables(&mut self) -> Result<()> { for table in ["bookmarks", "reading_list"] { let query = format!( "CREATE TABLE IF NOT EXISTS {} (link TEXT, comment TEXT, timestamp DATETIME)", table ); sqlx::query(&query).execute(&mut self.conn).await?; } Ok(()) } }