From 7fe20e6ae8a1172cc1ba26b3a7f5ad2ed726cf8c Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Tue, 13 Jun 2023 15:50:16 +0700 Subject: [PATCH] Run migrations on first creation, fix a bunch of panics --- .gitignore | 1 + migrations/tables.sql => sql/migrations.sql | 5 ++-- src/app.rs | 4 ++-- src/db.rs | 5 ++-- src/input.rs | 26 +++++++++++++-------- src/main.rs | 22 +++++++++++++---- 6 files changed, 43 insertions(+), 20 deletions(-) rename migrations/tables.sql => sql/migrations.sql (81%) diff --git a/.gitignore b/.gitignore index 092c176..060f1cd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /kanban.json /db.sqlite /kanban.db +/sql/queries.sql diff --git a/migrations/tables.sql b/sql/migrations.sql similarity index 81% rename from migrations/tables.sql rename to sql/migrations.sql index d5dc33b..2f88e2f 100644 --- a/migrations/tables.sql +++ b/sql/migrations.sql @@ -20,7 +20,8 @@ create table if not exists kb_column create table if not exists setting ( - id integer primary key autoincrement, - name text not null, + key text not null primary key, value text not null ); + +insert into kb_column(name) values ("Todo"),("InProgress"),("Done"),("Ideas"); diff --git a/src/app.rs b/src/app.rs index bd28b9c..eb38e89 100644 --- a/src/app.rs +++ b/src/app.rs @@ -145,7 +145,7 @@ impl<'a> Column { } pub fn select_last_task(&mut self) { - self.selected_task_idx = self.tasks.len() - 1; + self.selected_task_idx = self.tasks.len().saturating_sub(1); } pub fn move_task_up(&mut self) -> bool { @@ -160,7 +160,7 @@ impl<'a> Column { } pub fn move_task_down(&mut self) -> bool { - if self.selected_task_idx < self.tasks.len() - 1 { + if self.selected_task_idx < self.tasks.len().saturating_sub(1) { self.tasks .swap(self.selected_task_idx, self.selected_task_idx + 1); self.selected_task_idx += 1; diff --git a/src/db.rs b/src/db.rs index ba5d7e0..6014a05 100644 --- a/src/db.rs +++ b/src/db.rs @@ -83,9 +83,10 @@ pub fn move_task_to_column(conn: &Connection, task: &Task, target_column: &Colum "update task set column_id = ?2, - sort_order = 1 + + sort_order = coalesce(1 + (select sort_order from task - where column_id = ?2 order by sort_order desc limit 1) + where column_id = ?2 order by sort_order desc limit 1), + 0) where task.id = ?1", ) .unwrap(); diff --git a/src/input.rs b/src/input.rs index 6593e8d..5982773 100644 --- a/src/input.rs +++ b/src/input.rs @@ -78,16 +78,20 @@ pub fn handle(state: &mut State<'_>) -> Result<(), std::io::Error> { KeyCode::Char('g') => column.select_first_task(), KeyCode::Char('G') => column.select_last_task(), KeyCode::Char('H') => { - project.move_task_previous_column(); - let col = project.get_selected_column(); - let t = col.get_selected_task().unwrap(); - db::move_task_to_column(&state.db_conn, &t, &col); + if !column.tasks.is_empty() { + project.move_task_previous_column(); + let col = project.get_selected_column(); + let t = col.get_selected_task().unwrap(); + db::move_task_to_column(&state.db_conn, &t, &col); + } } KeyCode::Char('L') => { - project.move_task_next_column(); - let col = project.get_selected_column(); - let t = col.get_selected_task().unwrap(); - db::move_task_to_column(&state.db_conn, &t, &col); + if !column.tasks.is_empty() { + project.move_task_next_column(); + let col = project.get_selected_column(); + let t = col.get_selected_task().unwrap(); + db::move_task_to_column(&state.db_conn, &t, &col); + } } KeyCode::Char('J') => { if column.move_task_down() { @@ -108,8 +112,10 @@ pub fn handle(state: &mut State<'_>) -> Result<(), std::io::Error> { state.task_edit_state = column.get_task_state_from_curr_selected_task() } KeyCode::Char('D') => { - db::delete_task(&state.db_conn, column.get_selected_task().unwrap()); - column.remove_task(); + if !column.tasks.is_empty() { + db::delete_task(&state.db_conn, column.get_selected_task().unwrap()); + column.remove_task(); + } } _ => {} }, diff --git a/src/main.rs b/src/main.rs index 517790d..1736a1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,18 +15,32 @@ use tui::Terminal; /// kanban-tui is a simple, interactive TUI based task manager using kanban columns pub struct CliArgs { #[arg(value_name="DATABASE", value_hint=FilePath, index=1)] - /// Path to the + /// Path to the SQLite database pub filepath: Option, } +// TODO: We either make everything async or we remove the dependency #[async_std::main] async fn main() -> anyhow::Result<(), Box> { let dbpath = CliArgs::parse() .filepath - .map(PathBuf::into_os_string) - .unwrap_or("kanban.db".into()); + .unwrap_or(PathBuf::from("./kanban.db")); - let conn = Connection::open(dbpath)?; + let migrate = !dbpath.exists(); + + let mut conn = Connection::open(dbpath)?; + + if migrate { + let migrations = include_str!("../sql/migrations.sql"); + let migrations: Vec<&str> = migrations.split(";").collect(); + let tx = conn.transaction()?; + for m in migrations { + if !m.trim().is_empty() { + tx.execute(m, ())?; + } + } + tx.commit()?; + } let project = Project::load(&conn).await?; let mut state = State::new(conn, project);