use std::path::Path; use rusqlite::{Connection, ToSql, types::{FromSql, ValueRef, FromSqlResult}}; use chrono::DateTime; use crate::error; use crate::models::{Entry, Meta}; use crate::types::Time; pub trait Database { /// This is used to create tables and insert rows fn execute(&mut self, query: &str, params: &[&dyn ToSql]) -> error::Result<()>; /// And this is used to retrieve data fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result>; fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result>; // Entry queries fn entries_by_sheet(&self, sheet: &str) -> error::Result> { self.entry_query("select * from entries where sheet=?1 order by start asc", &[&sheet]) } fn entries_all_visible(&self) -> error::Result> { self.entry_query("select * from entries where sheet not like '!_%' escape \"!\" order by sheet asc, start asc", &[]) } fn entries_full(&self) -> error::Result> { self.entry_query("select * from entries order by sheet asc, start asc", &[]) } fn entry_insert(&mut self, at: Time, note: String) -> error::Result<()> { self.execute("", &[]) } // Meta queries fn current_sheet(&self) -> error::Result> { let results = self.meta_query("select * from meta where key=?1", &[&"current_sheet"])?; Ok(results.into_iter().next().map(|m| m.value)) } } pub struct SqliteDatabase { connection: Connection, } impl SqliteDatabase { pub fn from_memory() -> error::Result { Ok(SqliteDatabase { connection: Connection::open_in_memory()?, }) } pub fn from_path>(path: P) -> error::Result { Ok(SqliteDatabase { connection: Connection::open(path)?, }) } } impl Database for SqliteDatabase { fn execute(&mut self, query: &str, params: &[&dyn ToSql]) -> error::Result<()> { self.connection.execute(query, params)?; Ok(()) } fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result> { let mut stmt = self.connection.prepare(query)?; let results = stmt.query_map(params, |row| { let x = Ok(Entry { id: row.get("id")?, note: row.get("note")?, start: row.get("start")?, end: row.get("end")?, sheet: row.get("sheet")?, }); x})?.filter_map(|r| r.ok()).collect(); Ok(results) } fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result> { let mut stmt = self.connection.prepare(query)?; let results = stmt.query_map(params, |row| Ok(Meta { id: row.get("id")?, key: row.get("key")?, value: row.get("value")?, }))?.filter_map(|r| r.ok()).collect(); Ok(results) } } impl FromSql for Time { fn column_result(value: ValueRef<'_>) -> FromSqlResult { Ok(DateTime::column_result(value)?.into()) } }