Bubble up all db errors. Implement Deref and DerefMut for DBConn
This commit is contained in:
parent
f5b39592a4
commit
f951e500f1
107
src/app.rs
107
src/app.rs
@ -63,14 +63,13 @@ pub struct State<'a> {
|
||||
impl<'a> State<'a> {
|
||||
/// Creates a new [`State`].
|
||||
///
|
||||
/// # Panics
|
||||
/// # Errors
|
||||
///
|
||||
/// Panics if we can't get all the columns from the database
|
||||
#[must_use]
|
||||
/// Returns an error if we can't read the database columns
|
||||
pub fn new(conn: Connection) -> Result<Self, Error> {
|
||||
let db_conn = db::DBConn::new(conn);
|
||||
let columns = db_conn.get_all_columns()?;
|
||||
let selected_column = db_conn.get_selected_column();
|
||||
let selected_column = db_conn.get_selected_column()?;
|
||||
Ok(State {
|
||||
columns,
|
||||
selected_column_idx: selected_column,
|
||||
@ -89,14 +88,14 @@ impl<'a> State<'a> {
|
||||
&mut self.columns[self.selected_column_idx]
|
||||
}
|
||||
|
||||
pub fn select_previous_column(&mut self) {
|
||||
pub fn select_previous_column(&mut self) -> Result<(), Error> {
|
||||
self.selected_column_idx = self.selected_column_idx.saturating_sub(1);
|
||||
self.db_conn.set_selected_column(self.selected_column_idx);
|
||||
self.db_conn.set_selected_column(self.selected_column_idx)
|
||||
}
|
||||
|
||||
pub fn select_next_column(&mut self) {
|
||||
pub fn select_next_column(&mut self) -> Result<(), Error> {
|
||||
self.selected_column_idx = min(self.selected_column_idx + 1, self.columns.len() - 1);
|
||||
self.db_conn.set_selected_column(self.selected_column_idx);
|
||||
self.db_conn.set_selected_column(self.selected_column_idx)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -126,16 +125,18 @@ impl<'a> State<'a> {
|
||||
column.tasks.get_mut(column.selected_task_idx)
|
||||
}
|
||||
|
||||
pub fn select_previous_task(&mut self) {
|
||||
pub fn select_previous_task(&mut self) -> Result<(), Error> {
|
||||
let column = self.get_selected_column_mut();
|
||||
column.selected_task_idx = column.selected_task_idx.saturating_sub(1);
|
||||
|
||||
let task_idx = column.selected_task_idx;
|
||||
let col_id = column.id;
|
||||
self.db_conn.set_selected_task_for_column(task_idx, col_id);
|
||||
self.db_conn
|
||||
.set_selected_task_for_column(task_idx, col_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_next_task(&mut self) {
|
||||
pub fn select_next_task(&mut self) -> Result<(), Error> {
|
||||
let column = self.get_selected_column_mut();
|
||||
column.selected_task_idx = min(
|
||||
column.selected_task_idx + 1,
|
||||
@ -144,25 +145,31 @@ impl<'a> State<'a> {
|
||||
|
||||
let task_idx = column.selected_task_idx;
|
||||
let col_id = column.id;
|
||||
self.db_conn.set_selected_task_for_column(task_idx, col_id);
|
||||
self.db_conn
|
||||
.set_selected_task_for_column(task_idx, col_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_first_task(&mut self) {
|
||||
pub fn select_first_task(&mut self) -> Result<(), Error> {
|
||||
let column = self.get_selected_column_mut();
|
||||
column.selected_task_idx = 0;
|
||||
|
||||
let task_idx = column.selected_task_idx;
|
||||
let col_id = column.id;
|
||||
self.db_conn.set_selected_task_for_column(task_idx, col_id);
|
||||
self.db_conn
|
||||
.set_selected_task_for_column(task_idx, col_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_last_task(&mut self) {
|
||||
pub fn select_last_task(&mut self) -> Result<(), Error> {
|
||||
let column = self.get_selected_column_mut();
|
||||
column.selected_task_idx = column.tasks.len().saturating_sub(1);
|
||||
|
||||
let task_idx = column.selected_task_idx;
|
||||
let col_id = column.id;
|
||||
self.db_conn.set_selected_task_for_column(task_idx, col_id);
|
||||
self.db_conn
|
||||
.set_selected_task_for_column(task_idx, col_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -175,16 +182,16 @@ impl<'a> State<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn move_task_up(&mut self) {
|
||||
self.move_task(false);
|
||||
pub fn move_task_up(&mut self) -> Result<(), Error> {
|
||||
self.move_task(false)
|
||||
}
|
||||
|
||||
pub fn move_task_down(&mut self) {
|
||||
self.move_task(true);
|
||||
pub fn move_task_down(&mut self) -> Result<(), Error> {
|
||||
self.move_task(true)
|
||||
}
|
||||
|
||||
/// Returns the move task down of this [`State`].
|
||||
pub fn move_task(&mut self, is_down: bool) {
|
||||
pub fn move_task(&mut self, is_down: bool) -> Result<(), Error> {
|
||||
let other_task = if is_down {
|
||||
self.get_next_task()
|
||||
} else {
|
||||
@ -205,74 +212,80 @@ impl<'a> State<'a> {
|
||||
}
|
||||
|
||||
let col_id = column.id;
|
||||
self.db_conn.swap_task_order(t2_id, t1_id);
|
||||
self.db_conn.set_selected_task_for_column(task_idx, col_id);
|
||||
self.db_conn.swap_task_order(t2_id, t1_id)?;
|
||||
self.db_conn
|
||||
.set_selected_task_for_column(task_idx, col_id)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn move_task_previous_column(&mut self) {
|
||||
self.move_task_to_column(false);
|
||||
pub fn move_task_previous_column(&mut self) -> Result<(), Error> {
|
||||
self.move_task_to_column(false)
|
||||
}
|
||||
|
||||
pub fn move_task_next_column(&mut self) {
|
||||
self.move_task_to_column(true);
|
||||
pub fn move_task_next_column(&mut self) -> Result<(), Error> {
|
||||
self.move_task_to_column(true)
|
||||
}
|
||||
|
||||
fn move_task_to_column(&mut self, move_right: bool) {
|
||||
fn move_task_to_column(&mut self, move_right: bool) -> Result<(), Error> {
|
||||
let can_move_right = move_right && self.selected_column_idx < self.columns.len() - 1;
|
||||
let can_move_left = !move_right && self.selected_column_idx > 0;
|
||||
|
||||
let first_col = self.get_selected_column_mut();
|
||||
if first_col.tasks.is_empty() || !can_move_right && !can_move_left {
|
||||
return;
|
||||
// We're at the bounds so just ignore
|
||||
return Ok(());
|
||||
}
|
||||
let t = first_col.tasks.remove(first_col.selected_task_idx);
|
||||
|
||||
// Only move it if it was the last task
|
||||
if first_col.selected_task_idx == first_col.tasks.len() {
|
||||
self.select_previous_task();
|
||||
self.select_previous_task()?;
|
||||
}
|
||||
|
||||
if move_right {
|
||||
self.select_next_column();
|
||||
self.select_next_column()?;
|
||||
} else {
|
||||
self.select_previous_column();
|
||||
self.select_previous_column()?;
|
||||
}
|
||||
|
||||
let col = self.get_selected_column_mut();
|
||||
col.tasks.push(t);
|
||||
self.select_last_task();
|
||||
self.select_last_task()?;
|
||||
if let Some(task) = self.get_selected_task() {
|
||||
let col = self.get_selected_column();
|
||||
self.db_conn.move_task_to_column(task, col);
|
||||
self.db_conn.set_selected_column(self.selected_column_idx);
|
||||
self.db_conn.move_task_to_column(task, col)?;
|
||||
self.db_conn.set_selected_column(self.selected_column_idx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_new_task(&mut self, title: String, description: String) {
|
||||
pub fn add_new_task(&mut self, title: String, description: String) -> Result<(), Error> {
|
||||
let col_id = self.get_selected_column().id;
|
||||
let task = self.db_conn.insert_new_task(title, description, col_id);
|
||||
let task = self.db_conn.create_new_task(title, description, col_id)?;
|
||||
|
||||
self.select_last_task();
|
||||
self.select_last_task()?;
|
||||
let selected_task_idx = self.get_selected_column().selected_task_idx;
|
||||
self.db_conn
|
||||
.set_selected_task_for_column(selected_task_idx, col_id);
|
||||
.set_selected_task_for_column(selected_task_idx, col_id)?;
|
||||
|
||||
self.get_selected_column_mut().tasks.push(task);
|
||||
self.select_last_task()
|
||||
self.select_last_task()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn edit_task(&mut self, title: String, description: String) {
|
||||
pub fn edit_task(&mut self, title: String, description: String) -> Result<(), Error> {
|
||||
if let Some(selected_task) = self.get_selected_task_mut() {
|
||||
selected_task.title = title;
|
||||
selected_task.description = description;
|
||||
let cloned = selected_task.clone();
|
||||
self.db_conn.update_task_text(&cloned);
|
||||
self.db_conn.update_task_text(&cloned)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete the currently selected task from the selected column
|
||||
pub fn delete_task(&mut self) {
|
||||
pub fn delete_task(&mut self) -> Result<(), Error> {
|
||||
if let Some(task) = self.get_selected_task() {
|
||||
let task_id = task.id;
|
||||
let column = self.get_selected_column_mut();
|
||||
@ -282,12 +295,14 @@ impl<'a> State<'a> {
|
||||
column.tasks.remove(task_idx);
|
||||
|
||||
if column.selected_task_idx >= column.tasks.len() {
|
||||
self.select_previous_task();
|
||||
self.select_previous_task()?;
|
||||
task_idx = task_idx.saturating_sub(1);
|
||||
}
|
||||
|
||||
self.db_conn.delete_task(task_id);
|
||||
self.db_conn.set_selected_task_for_column(task_idx, col_id);
|
||||
self.db_conn.delete_task(task_id)?;
|
||||
self.db_conn
|
||||
.set_selected_task_for_column(task_idx, col_id)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
107
src/db.rs
107
src/db.rs
@ -1,8 +1,24 @@
|
||||
use anyhow::Error;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use crate::{Column, Task};
|
||||
use rusqlite::{params, Connection, Result};
|
||||
|
||||
pub struct DBConn(Connection);
|
||||
|
||||
impl DerefMut for DBConn {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DBConn {
|
||||
type Target = Connection;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DBConn {
|
||||
pub fn new(conn: Connection) -> Self {
|
||||
DBConn(conn)
|
||||
@ -14,7 +30,7 @@ impl DBConn {
|
||||
///
|
||||
/// This function will return an error if something is wrong with the SQL
|
||||
pub fn get_tasks_by_column(&self, column_name: &String) -> Result<Vec<Task>> {
|
||||
let mut stmt = self.0.prepare(
|
||||
let mut stmt = self.prepare(
|
||||
r#"
|
||||
select task.id, title, description from task
|
||||
join kb_column on column_id = kb_column.id
|
||||
@ -42,9 +58,7 @@ impl DBConn {
|
||||
///
|
||||
/// This function will return an error if there are issues with the SQL
|
||||
pub fn get_all_columns(&self) -> Result<Vec<Column>> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare("select id, name, selected_task from kb_column")?;
|
||||
let mut stmt = self.prepare("select id, name, selected_task from kb_column")?;
|
||||
let columns = stmt
|
||||
.query_map((), |row| {
|
||||
let name = row.get(1)?;
|
||||
@ -65,19 +79,21 @@ impl DBConn {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn insert_new_task(&self, title: String, description: String, column_id: i64) -> Task {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare("insert into task(title, description, column_id) values (?1, ?2, ?3)")
|
||||
.unwrap();
|
||||
stmt.execute(params![title, description, column_id])
|
||||
.unwrap();
|
||||
let id = self.0.last_insert_rowid();
|
||||
Task {
|
||||
pub fn create_new_task(
|
||||
&self,
|
||||
title: String,
|
||||
description: String,
|
||||
column_id: i64,
|
||||
) -> Result<Task> {
|
||||
let mut stmt =
|
||||
self.prepare("insert into task(title, description, column_id) values (?1, ?2, ?3)")?;
|
||||
stmt.execute(params![title, description, column_id])?;
|
||||
let id = self.last_insert_rowid();
|
||||
Ok(Task {
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// .
|
||||
@ -85,9 +101,10 @@ impl DBConn {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn delete_task(&self, task_id: i64) {
|
||||
let mut stmt = self.0.prepare("delete from task where id = ?1").unwrap();
|
||||
stmt.execute([task_id]).unwrap();
|
||||
pub fn delete_task(&self, task_id: i64) -> Result<()> {
|
||||
let mut stmt = self.prepare("delete from task where id = ?1")?;
|
||||
stmt.execute([task_id])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// .
|
||||
@ -95,13 +112,11 @@ impl DBConn {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn update_task_text(&self, task: &Task) {
|
||||
pub fn update_task_text(&self, task: &Task) -> Result<()> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare("update task set title = ?2, description = ?3 where id = ?1")
|
||||
.unwrap();
|
||||
stmt.execute((&task.id, &task.title, &task.description))
|
||||
.unwrap();
|
||||
.prepare("update task set title = ?2, description = ?3 where id = ?1")?;
|
||||
stmt.execute((&task.id, &task.title, &task.description))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// .
|
||||
@ -109,9 +124,8 @@ impl DBConn {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn move_task_to_column(&self, task: &Task, target_column: &Column) {
|
||||
pub fn move_task_to_column(&self, task: &Task, target_column: &Column) -> Result<()> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare(
|
||||
"update task
|
||||
set
|
||||
@ -124,7 +138,8 @@ impl DBConn {
|
||||
)
|
||||
.unwrap();
|
||||
stmt.execute((&task.id, &target_column.id)).unwrap();
|
||||
self.set_selected_task_for_column(target_column.selected_task_idx, target_column.id);
|
||||
self.set_selected_task_for_column(target_column.selected_task_idx, target_column.id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// .
|
||||
@ -132,8 +147,8 @@ impl DBConn {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn swap_task_order(&mut self, task1_id: i64, task2_id: i64) {
|
||||
let tx = self.0.transaction().unwrap();
|
||||
pub fn swap_task_order(&mut self, task1_id: i64, task2_id: i64) -> Result<()> {
|
||||
let tx = self.transaction().unwrap();
|
||||
|
||||
tx.execute(
|
||||
"create temp table temp_order as select sort_order from task where id = ?1",
|
||||
@ -156,54 +171,46 @@ impl DBConn {
|
||||
tx.execute("drop table temp_order", ()).unwrap();
|
||||
|
||||
tx.commit().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn set_selected_column(&self, column_id: usize) {
|
||||
pub fn set_selected_column(&self, column_id: usize) -> Result<(), Error> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare("insert or replace into app_state(key, value) values (?1, ?2)")
|
||||
.unwrap();
|
||||
stmt.execute((&"selected_column", column_id.to_string()))
|
||||
.unwrap();
|
||||
.prepare("insert or replace into app_state(key, value) values (?1, ?2)")?;
|
||||
stmt.execute((&"selected_column", column_id.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn get_selected_column(&self) -> usize {
|
||||
pub fn get_selected_column(&self) -> Result<usize> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare("select value from app_state where key = ?1")
|
||||
.unwrap();
|
||||
.prepare("select value from app_state where key = ?1")?;
|
||||
stmt.query_row(["selected_column"], |row| {
|
||||
let value: String = row.get::<usize, String>(0).unwrap();
|
||||
Ok(value.parse::<usize>().unwrap())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn set_selected_task_for_column(&self, task_idx: usize, column_id: i64) {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare("update kb_column set selected_task = ?2 where id = ?1")
|
||||
.unwrap();
|
||||
stmt.execute((column_id, task_idx)).unwrap();
|
||||
pub fn set_selected_task_for_column(&self, task_idx: usize, column_id: i64) -> Result<()> {
|
||||
let mut stmt = self.prepare("update kb_column set selected_task = ?2 where id = ?1")?;
|
||||
stmt.execute((column_id, task_idx))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if something goes wrong with the SQL
|
||||
pub fn get_selected_task_for_column(&self, column_id: i32) -> usize {
|
||||
pub fn get_selected_task_for_column(&self, column_id: i32) -> Result<usize> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare("select selected_task from kb_column where key = ?1")
|
||||
.unwrap();
|
||||
stmt.query_row([column_id], |row| row.get(0)).unwrap()
|
||||
.prepare("select selected_task from kb_column where key = ?1")?;
|
||||
stmt.query_row([column_id], |row| row.get(0))
|
||||
}
|
||||
}
|
||||
|
39
src/input.rs
39
src/input.rs
@ -2,8 +2,7 @@ use crate::app::{State, TaskEditFocus, TaskState};
|
||||
use crossterm::event;
|
||||
use crossterm::event::{Event, KeyCode};
|
||||
|
||||
// pub fn handle_task_edit(state: &mut State<'_>, key: event::KeyEvent, mut task: TaskState<'_>) {
|
||||
pub fn handle_task_edit(state: &mut State<'_>, key: event::KeyEvent) {
|
||||
pub fn handle_task_edit(state: &mut State<'_>, key: event::KeyEvent) -> Result<(), anyhow::Error> {
|
||||
if let Some(mut task) = state.task_edit_state.take() {
|
||||
let mut clear_task = false;
|
||||
match task.focus {
|
||||
@ -30,9 +29,9 @@ pub fn handle_task_edit(state: &mut State<'_>, key: event::KeyEvent) {
|
||||
let title = task.title.clone().into_lines().join("\n");
|
||||
let description = task.description.clone().into_lines().join("\n");
|
||||
if task.is_edit {
|
||||
state.edit_task(title, description);
|
||||
state.edit_task(title, description)?;
|
||||
} else {
|
||||
state.add_new_task(title, description);
|
||||
state.add_new_task(title, description)?;
|
||||
}
|
||||
clear_task = true;
|
||||
}
|
||||
@ -51,59 +50,61 @@ pub fn handle_task_edit(state: &mut State<'_>, key: event::KeyEvent) {
|
||||
state.task_edit_state = Some(task);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_main(state: &mut State<'_>, key: event::KeyEvent) {
|
||||
pub fn handle_main(state: &mut State<'_>, key: event::KeyEvent) -> Result<(), anyhow::Error> {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => state.quit = true,
|
||||
KeyCode::Char('h') | KeyCode::Left => {
|
||||
state.select_previous_column();
|
||||
state.select_previous_column()?;
|
||||
}
|
||||
KeyCode::Char('j') | KeyCode::Down => {
|
||||
state.select_next_task();
|
||||
state.select_next_task()?;
|
||||
}
|
||||
KeyCode::Char('k') | KeyCode::Up => {
|
||||
state.select_previous_task();
|
||||
state.select_previous_task()?;
|
||||
}
|
||||
KeyCode::Char('l') | KeyCode::Right => {
|
||||
state.select_next_column();
|
||||
state.select_next_column()?;
|
||||
}
|
||||
KeyCode::Char('g') => {
|
||||
state.select_first_task();
|
||||
state.select_first_task()?;
|
||||
}
|
||||
KeyCode::Char('G') => {
|
||||
state.select_last_task();
|
||||
state.select_last_task()?;
|
||||
}
|
||||
KeyCode::Char('H') => {
|
||||
state.move_task_previous_column();
|
||||
state.move_task_previous_column()?;
|
||||
}
|
||||
KeyCode::Char('L') => {
|
||||
state.move_task_next_column();
|
||||
state.move_task_next_column()?;
|
||||
}
|
||||
KeyCode::Char('J') => {
|
||||
state.move_task_down();
|
||||
state.move_task_down()?;
|
||||
}
|
||||
KeyCode::Char('K') => {
|
||||
state.move_task_up();
|
||||
state.move_task_up()?;
|
||||
}
|
||||
KeyCode::Char('n') => state.task_edit_state = Some(TaskState::default()),
|
||||
KeyCode::Char('e') => state.task_edit_state = state.get_task_state_from_current(),
|
||||
KeyCode::Char('D') => {
|
||||
state.delete_task();
|
||||
state.delete_task()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
/// Crossterm `event::read()` might return an error
|
||||
pub fn handle(state: &mut State<'_>) -> Result<(), std::io::Error> {
|
||||
pub fn handle(state: &mut State<'_>) -> Result<(), anyhow::Error> {
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if state.task_edit_state.is_some() {
|
||||
handle_task_edit(state, key);
|
||||
handle_task_edit(state, key)?;
|
||||
} else {
|
||||
handle_main(state, key);
|
||||
handle_main(state, key)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
Loading…
x
Reference in New Issue
Block a user