diff --git a/src/app.rs b/src/app.rs index 3825ac3..8419087 100644 --- a/src/app.rs +++ b/src/app.rs @@ -33,8 +33,15 @@ impl App { } }; + let mut error = String::new(); data_layer::setup(&con); - let tr_types = data_layer::get_transaction_types(&con); + let tr_types = match data_layer::get_transaction_types(&con) { + Ok(tt) => tt, + Err(e) => { + error = e.to_string(); + Vec::new() + } + }; return App { current_screen: CurrentScreen::Main, @@ -55,7 +62,11 @@ impl App { "Chart 3".to_string(), ] .to_vec(), - error: String::new(), + error: if error.is_empty() { + String::new() + } else { + error + }, error_showing: false, selected_tr_type_chart: 1, selected_tr_type_chart_desc: tr_types @@ -86,10 +97,16 @@ impl App { } pub fn get_list_trx(&mut self) -> Vec { - let accounts = self + return match self .trx_table - .get_trx(self.acc_list.get_selected_id(), &self.connection); - return accounts.to_vec(); + .get_trx(self.acc_list.get_selected_id(), &self.connection) + { + Ok(t) => t, + Err(e) => { + self.error = e.to_string(); + Vec::new() + } + }; } pub fn next_tr(&mut self) { @@ -120,7 +137,14 @@ impl App { pub fn save_new_tr(&mut self) { let new = self.new_transaction.get_id() == 0; self.next_tr_input(); - let tr = data_layer::upsert_transaction(&self.connection, self.new_transaction.clone()); + let tr = + match data_layer::upsert_transaction(&self.connection, self.new_transaction.clone()) { + Ok(tr) => tr, + Err(e) => { + self.error = e.to_string(); + return; + } + }; if new { self.trx_table.add_tr(tr); } @@ -305,19 +329,31 @@ impl App { } } - pub fn get_cumulative_total(&self) -> Vec<(f64, f64)> { - return data_layer::get_cumulative_account_sum( + pub fn get_cumulative_total(&mut self) -> Vec<(f64, f64)> { + return match data_layer::get_cumulative_account_sum( &self.connection, self.acc_list.get_selected_id(), - ); + ) { + Ok(d) => d, + Err(e) => { + self.error = e.to_string(); + Vec::new() + } + }; } - pub fn get_trtype_data(&self) -> Vec<(f64, f64)> { - return data_layer::get_tr_type_price_over_time( + pub fn get_trtype_data(&mut self) -> Vec<(f64, f64)> { + return match data_layer::get_tr_type_price_over_time( &self.connection, self.acc_list.get_selected_id(), self.selected_tr_type_chart, - ); + ) { + Ok(d) => d, + Err(e) => { + self.error = e.to_string(); + Vec::new() + } + }; } pub fn select_tr_type_chart(&mut self, id: i64) { diff --git a/src/data_layer.rs b/src/data_layer.rs index f62b984..078b03c 100644 --- a/src/data_layer.rs +++ b/src/data_layer.rs @@ -1,3 +1,5 @@ +use std::error::Error; + use crate::entities::*; use chrono::DateTime; use sqlite::{Connection, State}; @@ -82,28 +84,7 @@ pub fn upsert_account( )); } -// pub fn get_account(con: &Connection, id: i64) -> Account { -// let query = "SELECT * FROM Accounts WHERE id = ?"; -// let mut statement = con.prepare(query).unwrap(); -// statement.bind((1, id)).unwrap(); - -// if let Ok(State::Row) = statement.next() { -// return Account::new( -// statement.read::("id").unwrap(), -// statement.read::("name").unwrap(), -// statement.read::("asset_price").unwrap(), -// statement -// .read::("type") -// .unwrap() -// .try_into() -// .unwrap(), -// ); -// } else { -// return Account::new(0, "".to_string(), 0.0, AccountType::Cash); -// } -// } - -pub fn get_account_total(id: i64, con: &Connection) -> f64 { +pub fn get_account_total(id: i64, con: &Connection) -> Result> { let mut query = " SELECT SUM( @@ -119,16 +100,16 @@ JOIN Accounts a ON t.account_id = a.id" query.push_str(" WHERE account_id = :id") } - let mut statement = con.prepare(query).unwrap(); + let mut statement = con.prepare(query)?; if id != 0 { - statement.bind((":id", id)).unwrap(); + statement.bind((":id", id))?; } if let Ok(State::Row) = statement.next() { - return statement.read::("total").unwrap(); + return Ok(statement.read::("total")?); } else { - return 0.0; + return Ok(0.0); } } @@ -156,62 +137,40 @@ pub fn get_accounts(con: &Connection) -> Result, Box TransactionType { -// let query; -// if tt.get_id() == 0 { -// query = "INSERT INTO TransactionTypes (description) VALUES (?) RETURNING id;"; -// } else { -// query = "UPDATE TransactionTypes SET description = ? WHERE id = ? RETURNING id;"; -// } - -// let mut statement = con.prepare(query).unwrap(); -// statement.bind((1, &tt.get_description() as &str)).unwrap(); - -// if tt.get_id() != 0 { -// statement.bind((2, tt.get_id())).unwrap(); -// } - -// let id; -// if let Ok(State::Row) = statement.next() { -// id = statement.read::("id").unwrap(); -// } else { -// id = 0; -// } - -// return TransactionType::new(id, tt.get_description()); -// } - -pub fn get_transaction_type(con: &Connection, id: i64) -> TransactionType { +pub fn get_transaction_type(con: &Connection, id: i64) -> Result> { let query = "SELECT * FROM TransactionTypes WHERE id = ?"; - let mut statement = con.prepare(query).unwrap(); - statement.bind((1, id)).unwrap(); + let mut statement = con.prepare(query)?; + statement.bind((1, id))?; if let Ok(State::Row) = statement.next() { - return TransactionType::new( - statement.read::("id").unwrap(), - statement.read::("description").unwrap(), - ); + return Ok(TransactionType::new( + statement.read::("id")?, + statement.read::("description")?, + )); } else { - return TransactionType::new(0, "".to_string()); + return Ok(TransactionType::new(0, "".to_string())); } } -pub fn get_transaction_types(con: &Connection) -> Vec { +pub fn get_transaction_types(con: &Connection) -> Result, Box> { let query = "SELECT * FROM TransactionTypes"; - let mut statement = con.prepare(query).unwrap(); + let mut statement = con.prepare(query)?; let mut vec = Vec::::new(); while let Ok(State::Row) = statement.next() { vec.push(TransactionType::new( - statement.read::("id").unwrap(), - statement.read::("description").unwrap(), + statement.read::("id")?, + statement.read::("description")?, )); } - return vec; + return Ok(vec); } -pub fn upsert_transaction(con: &Connection, tr: Transaction) -> Transaction { +pub fn upsert_transaction( + con: &Connection, + tr: Transaction, +) -> Result> { let query; if tr.get_id() == 0 { query = "INSERT INTO Transactions @@ -224,32 +183,26 @@ pub fn upsert_transaction(con: &Connection, tr: Transaction) -> Transaction { WHERE id = :id RETURNING id;"; } - let mut statement = con.prepare(query).unwrap(); - statement.bind((":ac_id", tr.get_account_id())).unwrap(); - statement - .bind((":type_id", tr.get_type().get_id())) - .unwrap(); - statement.bind((":amnt", tr.get_amount())).unwrap(); - statement - .bind((":date", tr.get_date().timestamp())) - .unwrap(); - statement.bind((":desc", &tr.get_desc() as &str)).unwrap(); - statement - .bind((":asset_amnt", tr.get_asset_amnt())) - .unwrap(); + let mut statement = con.prepare(query)?; + statement.bind((":ac_id", tr.get_account_id()))?; + statement.bind((":type_id", tr.get_type().get_id()))?; + statement.bind((":amnt", tr.get_amount()))?; + statement.bind((":date", tr.get_date().timestamp()))?; + statement.bind((":desc", &tr.get_desc() as &str))?; + statement.bind((":asset_amnt", tr.get_asset_amnt()))?; if tr.get_id() != 0 { - statement.bind((":id", tr.get_id())).unwrap(); + statement.bind((":id", tr.get_id()))?; } let id; if let Ok(State::Row) = statement.next() { - id = statement.read::("id").unwrap(); + id = statement.read::("id")?; } else { id = 0; } - return Transaction::new( + return Ok(Transaction::new( id, tr.get_account_id(), tr.get_amount(), @@ -258,82 +211,64 @@ pub fn upsert_transaction(con: &Connection, tr: Transaction) -> Transaction { tr.get_type().get_id(), tr.get_asset_amnt(), con, - ); + )); } -// pub fn get_transaction(con: &Connection, id: i64) -> Transaction { -// let query = "SELECT * FROM Transactions WHERE id = :id"; -// let mut statement = con.prepare(query).unwrap(); -// statement.bind((":id", id)).unwrap(); - -// if let Ok(State::Row) = statement.next() { -// return Transaction::new( -// statement.read::("id").unwrap(), -// statement.read::("account_id").unwrap(), -// statement.read::("amount").unwrap(), -// DateTime::from_timestamp(statement.read::("date").unwrap(), 0).unwrap(), -// statement.read::("description").unwrap(), -// statement.read::("type_id").unwrap(), -// statement.read::("asset_amount").unwrap(), -// con, -// ); -// } else { -// return Transaction::new_empty(); -// } -// } - -pub fn get_account_transactions(con: &Connection, ac_id: i64) -> Vec { +pub fn get_account_transactions( + con: &Connection, + ac_id: i64, +) -> Result, Box> { if ac_id == 0 { return get_transactions(con); } let query = "SELECT * FROM Transactions WHERE account_id = :id ORDER BY Date DESC"; - let mut statement = con.prepare(query).unwrap(); - statement.bind((":id", ac_id)).unwrap(); + let mut statement = con.prepare(query)?; + statement.bind((":id", ac_id))?; let mut vec = Vec::::new(); while let Ok(State::Row) = statement.next() { vec.push(Transaction::new( - statement.read::("id").unwrap(), - statement.read::("account_id").unwrap(), - statement.read::("amount").unwrap(), - DateTime::from_timestamp(statement.read::("date").unwrap(), 0).unwrap(), - statement.read::("description").unwrap(), - statement.read::("type_id").unwrap(), - statement.read::("asset_amount").unwrap(), + statement.read::("id")?, + statement.read::("account_id")?, + statement.read::("amount")?, + DateTime::from_timestamp(statement.read::("date")?, 0).unwrap_or_default(), + statement.read::("description")?, + statement.read::("type_id")?, + statement.read::("asset_amount")?, con, )); } - return vec; + return Ok(vec); } -pub fn get_transactions(con: &Connection) -> Vec { +pub fn get_transactions(con: &Connection) -> Result, Box> { let query = "SELECT * FROM Transactions ORDER BY Date DESC"; - let mut statement = con.prepare(query).unwrap(); + let mut statement = con.prepare(query)?; let mut vec = Vec::::new(); while let Ok(State::Row) = statement.next() { vec.push(Transaction::new( - statement.read::("id").unwrap(), - statement.read::("account_id").unwrap(), - statement.read::("amount").unwrap(), - DateTime::from_timestamp(statement.read::("date").unwrap(), 0).unwrap(), - statement.read::("description").unwrap(), - statement.read::("type_id").unwrap(), - statement.read::("asset_amount").unwrap(), + statement.read::("id")?, + statement.read::("account_id")?, + statement.read::("amount")?, + DateTime::from_timestamp(statement.read::("date")?, 0).unwrap_or_default(), + statement.read::("description")?, + statement.read::("type_id")?, + statement.read::("asset_amount")?, con, )); } - return vec; + return Ok(vec); } pub fn get_tr_type_price_over_time( con: &Connection, ac_id: i64, tr_type_id: i64, -) -> Vec<(f64, f64)> { +) -> Result, Box> { let mut query = " SELECT strftime('%Y%m', datetime(date, 'unixepoch')) as z, SUM(amount) as y FROM Transactions @@ -345,24 +280,27 @@ WHERE type_id = :tr_type } query.push_str(" GROUP BY z;"); - let mut statement = con.prepare(query).unwrap(); - statement.bind((":tr_type", tr_type_id)).unwrap(); + let mut statement = con.prepare(query)?; + statement.bind((":tr_type", tr_type_id))?; if ac_id != 0 { - statement.bind((":ac_id", ac_id)).unwrap(); + statement.bind((":ac_id", ac_id))?; } let mut data = Vec::<(f64, f64)>::new(); let mut x = 0.0; while let Ok(State::Row) = statement.next() { - let y = statement.read::("y").unwrap() * -1.0; // Pour passer de dépense dans le négatif à un graphique qui monte + let y = statement.read::("y")? * -1.0; // Pour passer de dépense dans le négatif à un graphique qui monte data.push((x, y)); x += 1.0; } - return data; + return Ok(data); } -pub fn get_cumulative_account_sum(con: &Connection, ac_id: i64) -> Vec<(f64, f64)> { +pub fn get_cumulative_account_sum( + con: &Connection, + ac_id: i64, +) -> Result, Box> { let mut query = " SELECT SUM(amount) OVER ( @@ -375,18 +313,18 @@ FROM query.push_str(" WHERE account_id = :ac_id"); } - let mut statement = con.prepare(query).unwrap(); + let mut statement = con.prepare(query)?; if ac_id != 0 { - statement.bind((":ac_id", ac_id)).unwrap(); + statement.bind((":ac_id", ac_id))?; } let mut data = Vec::<(f64, f64)>::new(); let mut x = 0.0; while let Ok(State::Row) = statement.next() { - let y = statement.read::("y").unwrap(); + let y = statement.read::("y")?; data.push((x, y)); x += 1.0; } - return data; + return Ok(data); } diff --git a/src/entities/account.rs b/src/entities/account.rs index f88a81e..d38930a 100644 --- a/src/entities/account.rs +++ b/src/entities/account.rs @@ -1,4 +1,4 @@ -use std::convert::TryFrom; +use std::{convert::TryFrom, error::Error}; use sqlite::Connection; @@ -53,7 +53,7 @@ impl Account { pub fn set_type(&mut self, ac_type: AccountType) { self.ac_type = ac_type; } - pub fn get_total(&self, con: &Connection) -> f64 { + pub fn get_total(&self, con: &Connection) -> Result> { return data_layer::get_account_total(self.id, con); } } diff --git a/src/entities/transaction.rs b/src/entities/transaction.rs index f93b5b2..1965b1c 100644 --- a/src/entities/transaction.rs +++ b/src/entities/transaction.rs @@ -33,7 +33,10 @@ impl Transaction { date: date, description: desc, type_id: type_id, - tr_type: data_layer::get_transaction_type(con, type_id), + tr_type: data_layer::get_transaction_type(con, type_id).unwrap_or(TransactionType { + id: 0, + description: String::new(), + }), asset_amount: asset_amount, } } @@ -91,7 +94,10 @@ impl Transaction { } pub fn set_type(&mut self, tr_id: i64, con: &Connection) { self.type_id = tr_id; - self.tr_type = data_layer::get_transaction_type(con, tr_id); + self.tr_type = data_layer::get_transaction_type(con, tr_id).unwrap_or(TransactionType { + id: 0, + description: String::new(), + }); } pub fn get_asset_amnt(&self) -> f64 { diff --git a/src/entities/trxtable.rs b/src/entities/trxtable.rs index caec287..b81108f 100644 --- a/src/entities/trxtable.rs +++ b/src/entities/trxtable.rs @@ -1,3 +1,5 @@ +use std::error::Error; + use ratatui::widgets::TableState; use sqlite::Connection; @@ -19,15 +21,19 @@ impl TrxTable { }; } - pub fn get_trx(&mut self, ac_id: i64, con: &Connection) -> Vec { + pub fn get_trx( + &mut self, + ac_id: i64, + con: &Connection, + ) -> Result, Box> { if ac_id == -1 { - return Vec::new(); + return Ok(Vec::new()); } if self.trx.iter().count() == 0 || self.ac_id != ac_id { self.ac_id = ac_id; - self.trx = data_layer::get_account_transactions(con, ac_id); + self.trx = data_layer::get_account_transactions(con, ac_id)?; } - return self.trx.clone(); + return Ok(self.trx.clone()); } pub fn selected_tr(&self) -> Transaction { diff --git a/src/main.rs b/src/main.rs index 02fb034..fd303ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod enums; mod ui; use crate::{app::App, enums::*, ui::ui}; use chrono::Local; -use entities::{Account, AccountType, Transaction}; +use entities::{Account, Transaction}; use ratatui::{ Terminal, crossterm::{ diff --git a/src/ui.rs b/src/ui.rs index 15941fa..f0e9043 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -63,6 +63,7 @@ pub fn ui(frame: &mut Frame, app: &mut App) { "Total: {}", app.get_list_accounts()[i] .get_total(&app.connection) + .unwrap_or_default() .to_string() ) } else { @@ -116,14 +117,14 @@ pub fn ui(frame: &mut Frame, app: &mut App) { let min_x = slice .into_iter() .map(|&(x, _)| x) - .min_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap() + .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less)) + .unwrap_or_default() .round(); let max_x = slice .into_iter() .map(|&(x, _)| x) - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap() + .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater)) + .unwrap_or_default() .round() + 1.0; let x_axis = Axis::default() @@ -134,15 +135,15 @@ pub fn ui(frame: &mut Frame, app: &mut App) { let min_y = slice .into_iter() .map(|&(_, y)| y) - .min_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap() + .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less)) + .unwrap_or_default() .round() - 1.0; let max_y = slice .into_iter() .map(|&(_, y)| y) - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap() + .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater)) + .unwrap_or_default() .round() + 1.0; let y_axis = Axis::default() @@ -190,14 +191,14 @@ pub fn ui(frame: &mut Frame, app: &mut App) { let min_x = slice .into_iter() .map(|&(x, _)| x) - .min_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap() + .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less)) + .unwrap_or_default() .round(); let max_x = slice .into_iter() .map(|&(x, _)| x) - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap() + .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater)) + .unwrap_or_default() .round() + 1.0; let x_axis = Axis::default() @@ -208,15 +209,15 @@ pub fn ui(frame: &mut Frame, app: &mut App) { let min_y = slice .into_iter() .map(|&(_, y)| y) - .min_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap() + .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less)) + .unwrap_or_default() .round() - 1.0; let max_y = slice .into_iter() .map(|&(_, y)| y) - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap() + .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater)) + .unwrap_or_default() .round() + 1.0; let y_axis = Axis::default()