Replace FS with Sqlite #20
4 changed files with 102 additions and 27 deletions
|
@ -5,20 +5,22 @@ CREATE TABLE _tas_info (
|
||||||
CREATE TABLE tasks (
|
CREATE TABLE tasks (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
project TEXT NULL,
|
project TEXT ,
|
||||||
link TEXT NULL,
|
link TEXT ,
|
||||||
dir_path TEXT NULL,
|
dir_path TEXT ,
|
||||||
|
|
||||||
current BOOLEAN NOT NULL DEFAULT false,
|
current BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
created_at DATETIME NOT NULL DEFAULT datetime('now'),
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
finished_at DATETIME NULL
|
finished_at DATETIME
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE VIEW active_tasks AS (
|
CREATE VIEW active_tasks
|
||||||
SELECT t.*
|
AS
|
||||||
|
SELECT
|
||||||
|
t.*,
|
||||||
|
row_number() OVER (ORDER BY t.created_at) AS idx
|
||||||
FROM tasks AS t
|
FROM tasks AS t
|
||||||
WHERE t.finished_at IS NULL
|
WHERE t.finished_at IS NULL
|
||||||
ORDER BY t.created_at
|
ORDER BY t.created_at
|
||||||
);
|
|
||||||
;
|
;
|
||||||
|
|
|
@ -5,19 +5,22 @@ CREATE TABLE _tas_info (
|
||||||
CREATE TABLE tasks (
|
CREATE TABLE tasks (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
project TEXT NULL,
|
project TEXT ,
|
||||||
link TEXT NULL,
|
link TEXT ,
|
||||||
dir_path TEXT NULL,
|
dir_path TEXT ,
|
||||||
|
|
||||||
current BOOLEAN NOT NULL DEFAULT false,
|
current BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
created_at DATETIME NOT NULL DEFAULT datetime('now'),
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
finished_at DATETIME NULL
|
finished_at DATETIME
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE VIEW active_tasks AS (
|
CREATE VIEW active_tasks
|
||||||
SELECT t.*
|
AS
|
||||||
|
SELECT
|
||||||
|
t.*,
|
||||||
|
row_number() OVER (ORDER BY t.created_at) AS idx
|
||||||
FROM tasks AS t
|
FROM tasks AS t
|
||||||
WHERE t.finished_at IS NULL
|
WHERE t.finished_at IS NULL
|
||||||
ORDER BY t.created_at
|
ORDER BY t.created_at
|
||||||
);
|
;
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub enum Error {
|
||||||
NotFound,
|
NotFound,
|
||||||
InvalidData,
|
InvalidData,
|
||||||
InsertData,
|
InsertData,
|
||||||
|
RemoveData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
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::NotFound => f.write_str("Cannot find data"),
|
||||||
Error::InvalidData => f.write_str("Invalid data format"),
|
Error::InvalidData => f.write_str("Invalid data format"),
|
||||||
Error::InsertData => f.write_str("Cannot insert data"),
|
Error::InsertData => f.write_str("Cannot insert data"),
|
||||||
|
Error::RemoveData => f.write_str("Cannot remove data"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use rusqlite::Connection;
|
use rusqlite::{Connection, OptionalExtension};
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
use crate::domain;
|
use crate::domain;
|
||||||
|
@ -17,6 +17,8 @@ struct Task {
|
||||||
|
|
||||||
created_at: time::OffsetDateTime,
|
created_at: time::OffsetDateTime,
|
||||||
finished_at: Option<time::OffsetDateTime>,
|
finished_at: Option<time::OffsetDateTime>,
|
||||||
|
|
||||||
|
idx: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Task> for domain::Task {
|
impl From<Task> for domain::Task {
|
||||||
|
@ -43,6 +45,7 @@ impl<'r> TryFrom<&'r rusqlite::Row<'_>> for Task {
|
||||||
current: row.get("current")?,
|
current: row.get("current")?,
|
||||||
created_at: row.get("created_at")?,
|
created_at: row.get("created_at")?,
|
||||||
finished_at: row.get("finished_at")?,
|
finished_at: row.get("finished_at")?,
|
||||||
|
idx: row.get("idx")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,17 +60,53 @@ impl SqliteRepo {
|
||||||
pub fn new(xdg_dirs: BaseDirectories) -> Result<Self, Error> {
|
pub fn new(xdg_dirs: BaseDirectories) -> Result<Self, Error> {
|
||||||
let file_path = xdg_dirs.get_data_file(SCHEMA_FILE);
|
let file_path = xdg_dirs.get_data_file(SCHEMA_FILE);
|
||||||
let conn = Connection::open(file_path).map_err(|_| Error::Connect)?;
|
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 })
|
Ok(Self { conn })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repository for SqliteRepo {
|
impl Repository for SqliteRepo {
|
||||||
fn get_current_task_opt(&self) -> Result<Option<domain::CurrentTaskInfo>, Error> {
|
fn get_current_task_opt(&self) -> Result<Option<domain::CurrentTaskInfo>, 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<Option<domain::Task>, Error> {
|
fn get_task_opt(&self, id: domain::TaskId) -> Result<Option<domain::Task>, 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<Vec<domain::Task>, Error> {
|
fn get_tasks(&self) -> Result<Vec<domain::Task>, Error> {
|
||||||
|
@ -87,11 +126,40 @@ impl Repository for SqliteRepo {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_task(&self, id: domain::TaskId) -> Result<domain::Task, Error> {
|
fn remove_task(&self, id: domain::TaskId) -> Result<domain::Task, Error> {
|
||||||
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<domain::Task, Error> {
|
fn insert_task(&self, insert_data: super::InsertTaskData) -> Result<domain::Task, Error> {
|
||||||
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(
|
fn update_task(
|
||||||
|
|
Loading…
Reference in a new issue