Run migrations on first creation, fix a bunch of panics

This commit is contained in:
Joseph Ferano 2023-06-13 15:50:16 +07:00
parent 6cbba10325
commit 7fe20e6ae8
6 changed files with 43 additions and 20 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
/kanban.json /kanban.json
/db.sqlite /db.sqlite
/kanban.db /kanban.db
/sql/queries.sql

View File

@ -20,7 +20,8 @@ create table if not exists kb_column
create table if not exists setting create table if not exists setting
( (
id integer primary key autoincrement, key text not null primary key,
name text not null,
value text not null value text not null
); );
insert into kb_column(name) values ("Todo"),("InProgress"),("Done"),("Ideas");

View File

@ -145,7 +145,7 @@ impl<'a> Column {
} }
pub fn select_last_task(&mut self) { 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 { pub fn move_task_up(&mut self) -> bool {
@ -160,7 +160,7 @@ impl<'a> Column {
} }
pub fn move_task_down(&mut self) -> bool { 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 self.tasks
.swap(self.selected_task_idx, self.selected_task_idx + 1); .swap(self.selected_task_idx, self.selected_task_idx + 1);
self.selected_task_idx += 1; self.selected_task_idx += 1;

View File

@ -83,9 +83,10 @@ pub fn move_task_to_column(conn: &Connection, task: &Task, target_column: &Colum
"update task "update task
set set
column_id = ?2, column_id = ?2,
sort_order = 1 + sort_order = coalesce(1 +
(select sort_order from task (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", where task.id = ?1",
) )
.unwrap(); .unwrap();

View File

@ -78,17 +78,21 @@ pub fn handle(state: &mut State<'_>) -> Result<(), std::io::Error> {
KeyCode::Char('g') => column.select_first_task(), KeyCode::Char('g') => column.select_first_task(),
KeyCode::Char('G') => column.select_last_task(), KeyCode::Char('G') => column.select_last_task(),
KeyCode::Char('H') => { KeyCode::Char('H') => {
if !column.tasks.is_empty() {
project.move_task_previous_column(); project.move_task_previous_column();
let col = project.get_selected_column(); let col = project.get_selected_column();
let t = col.get_selected_task().unwrap(); let t = col.get_selected_task().unwrap();
db::move_task_to_column(&state.db_conn, &t, &col); db::move_task_to_column(&state.db_conn, &t, &col);
} }
}
KeyCode::Char('L') => { KeyCode::Char('L') => {
if !column.tasks.is_empty() {
project.move_task_next_column(); project.move_task_next_column();
let col = project.get_selected_column(); let col = project.get_selected_column();
let t = col.get_selected_task().unwrap(); let t = col.get_selected_task().unwrap();
db::move_task_to_column(&state.db_conn, &t, &col); db::move_task_to_column(&state.db_conn, &t, &col);
} }
}
KeyCode::Char('J') => { KeyCode::Char('J') => {
if column.move_task_down() { if column.move_task_down() {
let task1 = column.get_selected_task().unwrap(); let task1 = column.get_selected_task().unwrap();
@ -108,9 +112,11 @@ pub fn handle(state: &mut State<'_>) -> Result<(), std::io::Error> {
state.task_edit_state = column.get_task_state_from_curr_selected_task() state.task_edit_state = column.get_task_state_from_curr_selected_task()
} }
KeyCode::Char('D') => { KeyCode::Char('D') => {
if !column.tasks.is_empty() {
db::delete_task(&state.db_conn, column.get_selected_task().unwrap()); db::delete_task(&state.db_conn, column.get_selected_task().unwrap());
column.remove_task(); column.remove_task();
} }
}
_ => {} _ => {}
}, },
} }

View File

@ -15,18 +15,32 @@ use tui::Terminal;
/// kanban-tui is a simple, interactive TUI based task manager using kanban columns /// kanban-tui is a simple, interactive TUI based task manager using kanban columns
pub struct CliArgs { pub struct CliArgs {
#[arg(value_name="DATABASE", value_hint=FilePath, index=1)] #[arg(value_name="DATABASE", value_hint=FilePath, index=1)]
/// Path to the /// Path to the SQLite database
pub filepath: Option<PathBuf>, pub filepath: Option<PathBuf>,
} }
// TODO: We either make everything async or we remove the dependency
#[async_std::main] #[async_std::main]
async fn main() -> anyhow::Result<(), Box<dyn Error>> { async fn main() -> anyhow::Result<(), Box<dyn Error>> {
let dbpath = CliArgs::parse() let dbpath = CliArgs::parse()
.filepath .filepath
.map(PathBuf::into_os_string) .unwrap_or(PathBuf::from("./kanban.db"));
.unwrap_or("kanban.db".into());
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 project = Project::load(&conn).await?;
let mut state = State::new(conn, project); let mut state = State::new(conn, project);