Replace FS with Sqlite #20
4 changed files with 126 additions and 45 deletions
|
@ -15,7 +15,7 @@
|
||||||
//!
|
//!
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub type TaskId = usize;
|
pub type TaskIdx = usize;
|
||||||
|
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
10
src/repo.rs
10
src/repo.rs
|
@ -28,6 +28,7 @@ pub enum Error {
|
||||||
NotFound,
|
NotFound,
|
||||||
InvalidData,
|
InvalidData,
|
||||||
InsertData,
|
InsertData,
|
||||||
|
UpdateData,
|
||||||
RemoveData,
|
RemoveData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +41,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::UpdateData => f.write_str("Cannot update data"),
|
||||||
Error::RemoveData => f.write_str("Cannot remove data"),
|
Error::RemoveData => f.write_str("Cannot remove data"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,21 +67,21 @@ pub struct UpdateTaskData {
|
||||||
pub trait Repository {
|
pub trait Repository {
|
||||||
fn get_current_task_opt(&self) -> Result<Option<domain::CurrentTaskInfo>, Error>;
|
fn get_current_task_opt(&self) -> Result<Option<domain::CurrentTaskInfo>, Error>;
|
||||||
|
|
||||||
fn get_task_opt(&self, id: domain::TaskId) -> Result<Option<domain::Task>, Error>;
|
fn get_task_opt(&self, id: domain::TaskIdx) -> Result<Option<domain::Task>, Error>;
|
||||||
|
|
||||||
fn get_tasks(&self) -> Result<Vec<domain::Task>, Error>;
|
fn get_tasks(&self) -> Result<Vec<domain::Task>, Error>;
|
||||||
|
|
||||||
fn remove_task(&self, id: domain::TaskId) -> Result<domain::Task, Error>;
|
fn remove_task(&self, id: domain::TaskIdx) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
fn update_task(
|
fn update_task(
|
||||||
&self,
|
&self,
|
||||||
id: domain::TaskId,
|
id: domain::TaskIdx,
|
||||||
update_data: UpdateTaskData,
|
update_data: UpdateTaskData,
|
||||||
) -> Result<domain::Task, Error>;
|
) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
fn insert_task(&self, insert_data: InsertTaskData) -> Result<domain::Task, Error>;
|
fn insert_task(&self, insert_data: InsertTaskData) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
fn start_task(&self, id: domain::TaskId) -> Result<domain::Task, Error>;
|
fn start_task(&self, id: domain::TaskIdx) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
fn stop_task(&self) -> Result<domain::Task, Error>;
|
fn stop_task(&self) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ impl Repository for FsRepo {
|
||||||
.map(|cur_task| cur_task.map(From::from))
|
.map(|cur_task| cur_task.map(From::from))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_task_opt(&self, id: domain::TaskId) -> Result<Option<domain::Task>, Error> {
|
fn get_task_opt(&self, id: domain::TaskIdx) -> Result<Option<domain::Task>, Error> {
|
||||||
let tasks = self.get_tasks_impl()?;
|
let tasks = self.get_tasks_impl()?;
|
||||||
if id == 0 || id > tasks.len() {
|
if id == 0 || id > tasks.len() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
|
@ -93,7 +93,7 @@ impl Repository for FsRepo {
|
||||||
.map(|tasks| tasks.into_iter().map(Task::into).collect())
|
.map(|tasks| tasks.into_iter().map(Task::into).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_task(&self, id: domain::TaskId) -> Result<domain::Task, Error> {
|
fn remove_task(&self, id: domain::TaskIdx) -> Result<domain::Task, Error> {
|
||||||
let mut tasks = self.get_tasks_impl()?;
|
let mut tasks = self.get_tasks_impl()?;
|
||||||
if id == 0 || id > tasks.len() {
|
if id == 0 || id > tasks.len() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
|
@ -107,7 +107,7 @@ impl Repository for FsRepo {
|
||||||
|
|
||||||
fn update_task(
|
fn update_task(
|
||||||
&self,
|
&self,
|
||||||
id: domain::TaskId,
|
id: domain::TaskIdx,
|
||||||
update_data: UpdateTaskData,
|
update_data: UpdateTaskData,
|
||||||
) -> Result<domain::Task, Error> {
|
) -> Result<domain::Task, Error> {
|
||||||
let mut tasks = self.get_tasks_impl()?;
|
let mut tasks = self.get_tasks_impl()?;
|
||||||
|
@ -161,7 +161,7 @@ impl Repository for FsRepo {
|
||||||
Ok(new_task.into())
|
Ok(new_task.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_task(&self, id: domain::TaskId) -> Result<domain::Task, Error> {
|
fn start_task(&self, id: domain::TaskIdx) -> Result<domain::Task, Error> {
|
||||||
let tasks = self.get_tasks_impl()?;
|
let tasks = self.get_tasks_impl()?;
|
||||||
if id == 0 || id > tasks.len() {
|
if id == 0 || id > tasks.len() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
|
|
|
@ -12,12 +12,11 @@ struct Task {
|
||||||
project: Option<String>,
|
project: Option<String>,
|
||||||
link: Option<String>,
|
link: Option<String>,
|
||||||
dir_path: Option<String>,
|
dir_path: Option<String>,
|
||||||
|
/*
|
||||||
current: bool,
|
current: bool,
|
||||||
|
|
||||||
created_at: time::OffsetDateTime,
|
created_at: time::OffsetDateTime,
|
||||||
finished_at: Option<time::OffsetDateTime>,
|
finished_at: Option<time::OffsetDateTime>,
|
||||||
|
*/
|
||||||
idx: i64,
|
idx: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,9 +41,11 @@ impl<'r> TryFrom<&'r rusqlite::Row<'_>> for Task {
|
||||||
project: row.get("project")?,
|
project: row.get("project")?,
|
||||||
link: row.get("link")?,
|
link: row.get("link")?,
|
||||||
dir_path: row.get("dir_path")?,
|
dir_path: row.get("dir_path")?,
|
||||||
|
/*
|
||||||
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")?,
|
idx: row.get("idx")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -79,34 +80,16 @@ impl SqliteRepo {
|
||||||
|
|
||||||
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> {
|
||||||
let mut stmt = self
|
let db_task = self.get_current_task_opt_impl()?;
|
||||||
.conn
|
|
||||||
.prepare("SELECT * FROM active_tasks WHERE current IS true")
|
|
||||||
.map_err(|_| Error::PrepareQuery)?;
|
|
||||||
|
|
||||||
let row = stmt
|
Ok(db_task.map(|db_task| domain::CurrentTaskInfo {
|
||||||
.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_idx: db_task.idx as usize,
|
||||||
task: db_task.into(),
|
task: db_task.into(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_task_opt(&self, id: domain::TaskId) -> Result<Option<domain::Task>, Error> {
|
fn get_task_opt(&self, id: domain::TaskIdx) -> Result<Option<domain::Task>, Error> {
|
||||||
let mut stmt = self
|
self.get_task_opt_impl(id).map(|t| t.map(From::from))
|
||||||
.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> {
|
||||||
|
@ -125,18 +108,18 @@ impl Repository for SqliteRepo {
|
||||||
.map_err(|_| Error::InvalidData)
|
.map_err(|_| Error::InvalidData)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_task(&self, id: domain::TaskId) -> Result<domain::Task, Error> {
|
fn remove_task(&self, idx: domain::TaskIdx) -> Result<domain::Task, Error> {
|
||||||
let task = self.get_task_opt(id)?.ok_or(Error::NotFound)?;
|
let db_task = self.get_task_opt_impl(idx)?.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.conn
|
.conn
|
||||||
.prepare("DELETE FROM tasks WHERE id = ?")
|
.prepare("DELETE FROM tasks WHERE id = ?")
|
||||||
.map_err(|_| Error::PrepareQuery)?;
|
.map_err(|_| Error::PrepareQuery)?;
|
||||||
|
|
||||||
stmt.execute([&(id as i64)])
|
stmt.execute([&(db_task.id)])
|
||||||
.map_err(|_| Error::RemoveData)?;
|
.map_err(|_| Error::RemoveData)?;
|
||||||
|
|
||||||
Ok(task)
|
Ok(db_task.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_task(&self, insert_data: super::InsertTaskData) -> Result<domain::Task, Error> {
|
fn insert_task(&self, insert_data: super::InsertTaskData) -> Result<domain::Task, Error> {
|
||||||
|
@ -159,26 +142,122 @@ impl Repository for SqliteRepo {
|
||||||
))
|
))
|
||||||
.map_err(|_| Error::InsertData)?;
|
.map_err(|_| Error::InsertData)?;
|
||||||
|
|
||||||
self.get_task_opt(id as usize)?.ok_or(Error::NotFound)
|
dbg!(id);
|
||||||
|
|
||||||
|
self.get_task_by_id_impl(id).map(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_task(
|
fn update_task(
|
||||||
&self,
|
&self,
|
||||||
id: domain::TaskId,
|
idx: domain::TaskIdx,
|
||||||
update_data: super::UpdateTaskData,
|
update_data: super::UpdateTaskData,
|
||||||
) -> Result<domain::Task, Error> {
|
) -> Result<domain::Task, Error> {
|
||||||
todo!()
|
let task = self.get_task_opt_impl(idx)?.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
|
let mut stmt = self
|
||||||
|
.conn
|
||||||
|
.prepare(
|
||||||
|
"UPDATE tasks
|
||||||
|
SET name = ?1,
|
||||||
|
project = ?2,
|
||||||
|
link = ?3,
|
||||||
|
dir_path = ?4
|
||||||
|
WHERE id = ?5
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.map_err(|_| Error::PrepareQuery)?;
|
||||||
|
|
||||||
|
stmt.execute((
|
||||||
|
&update_data.name.unwrap_or(task.name),
|
||||||
|
&update_data.project.unwrap_or(task.project),
|
||||||
|
&update_data.link.unwrap_or(task.link),
|
||||||
|
&update_data
|
||||||
|
.dir_path
|
||||||
|
.unwrap_or(task.dir_path.map(PathBuf::from))
|
||||||
|
.and_then(|p| p.into_os_string().into_string().ok()),
|
||||||
|
&task.id,
|
||||||
|
))
|
||||||
|
.map_err(|_| Error::UpdateData)?;
|
||||||
|
|
||||||
|
self.get_task_opt(idx)?.ok_or(Error::NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_task(&self, id: domain::TaskId) -> Result<domain::Task, Error> {
|
fn start_task(&self, idx: domain::TaskIdx) -> Result<domain::Task, Error> {
|
||||||
todo!()
|
let db_task = self.get_task_opt_impl(idx)?.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
|
self.conn
|
||||||
|
.execute(
|
||||||
|
"UPDATE tasks SET current = true WHERE id = ?1",
|
||||||
|
[&db_task.id],
|
||||||
|
)
|
||||||
|
.map_err(|_| Error::UpdateData)?;
|
||||||
|
|
||||||
|
Ok(db_task.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_task(&self) -> Result<domain::Task, Error> {
|
fn stop_task(&self) -> Result<domain::Task, Error> {
|
||||||
todo!()
|
let db_task = self.get_current_task_opt_impl()?.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
|
self.conn
|
||||||
|
.execute(
|
||||||
|
"UPDATE tasks SET current = false WHERE id = ?1",
|
||||||
|
[&db_task.id],
|
||||||
|
)
|
||||||
|
.map_err(|_| Error::UpdateData)?;
|
||||||
|
|
||||||
|
Ok(db_task.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_task(&self) -> Result<domain::Task, Error> {
|
fn finish_task(&self) -> Result<domain::Task, Error> {
|
||||||
todo!()
|
let db_task = self.get_current_task_opt_impl()?.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
|
self.conn
|
||||||
|
.execute(
|
||||||
|
"UPDATE tasks
|
||||||
|
SET current = false,
|
||||||
|
finished_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = ?1",
|
||||||
|
[&db_task.id],
|
||||||
|
)
|
||||||
|
.map_err(|_| Error::UpdateData)?;
|
||||||
|
|
||||||
|
Ok(db_task.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SqliteRepo {
|
||||||
|
fn get_task_by_id_impl(&self, id: i64) -> Result<Task, Error> {
|
||||||
|
let mut stmt = self
|
||||||
|
.conn
|
||||||
|
.prepare("SELECT * FROM active_tasks WHERE id = ?")
|
||||||
|
.map_err(|_| Error::PrepareQuery)?;
|
||||||
|
|
||||||
|
stmt.query_row([id], |row| Task::try_from(row))
|
||||||
|
.map_err(|_| Error::QueryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_task_opt_impl(&self, idx: domain::TaskIdx) -> Result<Option<Task>, Error> {
|
||||||
|
let mut stmt = self
|
||||||
|
.conn
|
||||||
|
.prepare("SELECT * FROM active_tasks WHERE idx = ?")
|
||||||
|
.map_err(|_| Error::PrepareQuery)?;
|
||||||
|
|
||||||
|
stmt.query_row([idx as i64], |row| Task::try_from(row))
|
||||||
|
.optional()
|
||||||
|
.map_err(|_| Error::QueryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_task_opt_impl(&self) -> Result<Option<Task>, Error> {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue