diff --git a/database/migrations/202208162308.sql b/database/migrations/202208162308.sql index 9d549d5..981c7bb 100644 --- a/database/migrations/202208162308.sql +++ b/database/migrations/202208162308.sql @@ -5,20 +5,22 @@ CREATE TABLE _tas_info ( CREATE TABLE tasks ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, - project TEXT NULL, - link TEXT NULL, - dir_path TEXT NULL, + project TEXT , + link TEXT , + dir_path TEXT , current BOOLEAN NOT NULL DEFAULT false, - created_at DATETIME NOT NULL DEFAULT datetime('now'), - finished_at DATETIME NULL + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + finished_at DATETIME ); -CREATE VIEW active_tasks AS ( - SELECT t.* - FROM tasks AS t - WHERE t.finished_at IS NULL - ORDER BY t.created_at -); +CREATE VIEW active_tasks +AS +SELECT + t.*, + row_number() OVER (ORDER BY t.created_at) AS idx +FROM tasks AS t +WHERE t.finished_at IS NULL +ORDER BY t.created_at ; diff --git a/database/schema.sql b/database/schema.sql index 77cda19..981c7bb 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -5,19 +5,22 @@ CREATE TABLE _tas_info ( CREATE TABLE tasks ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, - project TEXT NULL, - link TEXT NULL, - dir_path TEXT NULL, + project TEXT , + link TEXT , + dir_path TEXT , current BOOLEAN NOT NULL DEFAULT false, - created_at DATETIME NOT NULL DEFAULT datetime('now'), - finished_at DATETIME NULL + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + finished_at DATETIME ); -CREATE VIEW active_tasks AS ( - SELECT t.* - FROM tasks AS t - WHERE t.finished_at IS NULL - ORDER BY t.created_at -); +CREATE VIEW active_tasks +AS +SELECT + t.*, + row_number() OVER (ORDER BY t.created_at) AS idx +FROM tasks AS t +WHERE t.finished_at IS NULL +ORDER BY t.created_at +; diff --git a/src/repo.rs b/src/repo.rs index d266e98..c75ec0b 100755 --- a/src/repo.rs +++ b/src/repo.rs @@ -28,6 +28,7 @@ pub enum Error { NotFound, InvalidData, InsertData, + RemoveData, } impl std::fmt::Display for Error { @@ -39,6 +40,7 @@ impl std::fmt::Display for Error { Error::NotFound => f.write_str("Cannot find data"), Error::InvalidData => f.write_str("Invalid data format"), Error::InsertData => f.write_str("Cannot insert data"), + Error::RemoveData => f.write_str("Cannot remove data"), } } } diff --git a/src/repo/sqlite.rs b/src/repo/sqlite.rs index 3846d22..ca2ded5 100644 --- a/src/repo/sqlite.rs +++ b/src/repo/sqlite.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use rusqlite::Connection; +use rusqlite::{Connection, OptionalExtension}; use xdg::BaseDirectories; use crate::domain; @@ -17,6 +17,8 @@ struct Task { created_at: time::OffsetDateTime, finished_at: Option, + + idx: i64, } impl From for domain::Task { @@ -43,6 +45,7 @@ impl<'r> TryFrom<&'r rusqlite::Row<'_>> for Task { current: row.get("current")?, created_at: row.get("created_at")?, finished_at: row.get("finished_at")?, + idx: row.get("idx")?, }) } } @@ -57,17 +60,53 @@ impl SqliteRepo { pub fn new(xdg_dirs: BaseDirectories) -> Result { let file_path = xdg_dirs.get_data_file(SCHEMA_FILE); let conn = Connection::open(file_path).map_err(|_| Error::Connect)?; + + /* + conn.execute_batch(&format!( + "BEGIN; {} COMMIT;", + include_str!("../../database/schema.sql") + )) + .map_err(|err| match err { + rusqlite::Error::SqlInputError { .. } => panic!("{}", err), + _ => err, + }) + .ok(); + */ + Ok(Self { conn }) } } impl Repository for SqliteRepo { fn get_current_task_opt(&self) -> Result, Error> { - todo!() + let mut stmt = self + .conn + .prepare("SELECT * FROM active_tasks WHERE current IS true") + .map_err(|_| Error::PrepareQuery)?; + + let row = stmt + .query_row([], |row| Task::try_from(row)) + .optional() + .map_err(|_| Error::QueryData)?; + + Ok(row.map(|db_task| domain::CurrentTaskInfo { + task_idx: db_task.idx as usize, + task: db_task.into(), + })) } fn get_task_opt(&self, id: domain::TaskId) -> Result, Error> { - todo!() + let mut stmt = self + .conn + .prepare("SELECT * FROM active_tasks WHERE idx = ?") + .map_err(|_| Error::PrepareQuery)?; + + let row = stmt + .query_row([id as i64], |row| Task::try_from(row)) + .optional() + .map_err(|_| Error::QueryData)?; + + Ok(row.map(From::from)) } fn get_tasks(&self) -> Result, Error> { @@ -87,11 +126,40 @@ impl Repository for SqliteRepo { } fn remove_task(&self, id: domain::TaskId) -> Result { - todo!() + let task = self.get_task_opt(id)?.ok_or(Error::NotFound)?; + + let mut stmt = self + .conn + .prepare("DELETE FROM tasks WHERE id = ?") + .map_err(|_| Error::PrepareQuery)?; + + stmt.execute([&(id as i64)]) + .map_err(|_| Error::RemoveData)?; + + Ok(task) } fn insert_task(&self, insert_data: super::InsertTaskData) -> Result { - todo!() + let mut stmt = self + .conn + .prepare( + "INSERT INTO tasks (name, project, link, dir_path) + VALUES (?1, ?2, ?3, ?4)", + ) + .map_err(|_| Error::PrepareQuery)?; + + let id = stmt + .insert(( + &insert_data.name, + &insert_data.project, + &insert_data.link, + &insert_data + .dir_path + .and_then(|p| p.into_os_string().into_string().ok()), + )) + .map_err(|_| Error::InsertData)?; + + self.get_task_opt(id as usize)?.ok_or(Error::NotFound) } fn update_task(