Replace FS with Sqlite #20

Merged
pleshevskiy merged 8 commits from sqlite into main 2022-08-20 15:42:20 +03:00
4 changed files with 126 additions and 45 deletions
Showing only changes of commit 62ad59ab76 - Show all commits

View file

@ -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,

View file

@ -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>;

View file

@ -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);

View file

@ -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)
} }
} }