From 703496a49315f7b67442abf0e0df3c41d3605d3b Mon Sep 17 00:00:00 2001 From: Joe Carstairs Date: Fri, 15 Aug 2025 16:02:40 +0000 Subject: task-012 (#2) Co-authored-by: Joe Carstairs Reviewed-on: https://git.joeac.net/joeac/schist/pulls/2 Co-authored-by: Joe Carstairs Co-committed-by: Joe Carstairs --- schist_desktop_gui/src/connection.rs | 20 +++++++ schist_desktop_gui/src/dummy_data.rs | 70 ---------------------- schist_desktop_gui/src/gui.rs | 47 +++++++++++---- .../src/gui/components/bucket_name_and_balance.rs | 6 +- .../src/gui/components/transactions_view.rs | 2 +- schist_desktop_gui/src/main.rs | 32 +++++++--- 6 files changed, 83 insertions(+), 94 deletions(-) create mode 100644 schist_desktop_gui/src/connection.rs delete mode 100644 schist_desktop_gui/src/dummy_data.rs (limited to 'schist_desktop_gui/src') diff --git a/schist_desktop_gui/src/connection.rs b/schist_desktop_gui/src/connection.rs new file mode 100644 index 0000000..5a88083 --- /dev/null +++ b/schist_desktop_gui/src/connection.rs @@ -0,0 +1,20 @@ +use std::{fs::create_dir_all, path::Path}; + +use anyhow::Context; +use diesel::{Connection, SqliteConnection}; + +const SCHIST_DATA_DIR: &'static str = "schist"; +const SCHIST_DATABASE_FILENAME: &'static str = "schist.sqlite"; + +pub fn establish_connection() -> anyhow::Result { + let data_dir = dirs::data_dir().context("Failed to get local app data directory")?; + let schist_local_dir = data_dir.join(Path::new(SCHIST_DATA_DIR)); + create_dir_all(&schist_local_dir) + .context(format!("Failed to create local files directory at {}", schist_local_dir.display()))?; + let db_path = schist_local_dir.join(Path::new(SCHIST_DATABASE_FILENAME)); + let db_path = db_path + .to_str() + .context("Failed to construct database path")?; + Ok(SqliteConnection::establish(db_path) + .with_context(|| format!("Failed to connect to database at {}", db_path))?) +} diff --git a/schist_desktop_gui/src/dummy_data.rs b/schist_desktop_gui/src/dummy_data.rs deleted file mode 100644 index 079b687..0000000 --- a/schist_desktop_gui/src/dummy_data.rs +++ /dev/null @@ -1,70 +0,0 @@ -use schist_models::{ - account::Account, bucket::Bucket, budget_period_unit::BudgetPeriodUnit, date_utc::DateUtc, -}; - -#[derive(Clone, Debug)] -pub struct Error {} - -impl From for Error { - fn from(_value: anyhow::Error) -> Self { - Error {} - } -} - -pub async fn fetch_accounts() -> Result, Error> { - Ok(vec![ - Account { - id: 0, - name: String::from("Nationwide current account"), - opening_balance: 0, - opening_date: DateUtc::from_ymd(2024, 06, 12)?, - }, - Account { - id: 1, - name: String::from("cash account"), - opening_balance: 0, - opening_date: DateUtc::from_ymd(2024, 06, 12)?, - }, - Account { - id: 2, - name: String::from("YBS savings account"), - opening_balance: 0, - opening_date: DateUtc::from_ymd(2024, 06, 12)?, - }, - ]) -} - -pub async fn fetch_buckets() -> Result, Error> { - Ok(vec![ - Bucket { - id: 0, - name: String::from("groceries"), - balance: 123, - balance_date: DateUtc::from_ymd(2025, 8, 10)?, - budget_period: 1, - budget_period_unit: BudgetPeriodUnit::Month, - budget_quantity: 300, - group: String::from("normal expenses"), - }, - Bucket { - id: 1, - name: String::from("outdoor trips"), - balance: -11, - balance_date: DateUtc::from_ymd(2025, 8, 10)?, - budget_period: 1, - budget_period_unit: BudgetPeriodUnit::Month, - budget_quantity: 110, - group: String::from("outdoors"), - }, - Bucket { - id: 2, - name: String::from("salary"), - balance: 1018, - balance_date: DateUtc::from_ymd(2025, 8, 10)?, - budget_period: 1, - budget_period_unit: BudgetPeriodUnit::Month, - budget_quantity: -2196, - group: String::from("income"), - }, - ]) -} diff --git a/schist_desktop_gui/src/gui.rs b/schist_desktop_gui/src/gui.rs index 0f84f41..d22e999 100644 --- a/schist_desktop_gui/src/gui.rs +++ b/schist_desktop_gui/src/gui.rs @@ -1,12 +1,13 @@ pub mod components; pub mod screens; +use diesel::SqliteConnection; use iced::Element; use schist_models::{account::Account, bucket::Bucket}; +use schist_queries::{accounts::get_all_accounts, buckets::get_all_buckets}; use crate::{ config::Config, - dummy_data::{self, fetch_accounts, fetch_buckets}, gui::screens::{main_screen, MainScreen, Screen}, shortcut::KeyBind, traits::{Component, Viewable}, @@ -16,8 +17,23 @@ use crate::{ pub enum Message { Event(iced::Event), MainScreenMessage(main_screen::Message), - RefetchedAccounts(Result, dummy_data::Error>), - RefetchedBuckets(Result, dummy_data::Error>), + RefetchedAccounts(FetchResult>), + RefetchedBuckets(FetchResult>), +} + +pub type FetchResult = std::result::Result; + +#[derive(Clone, Debug)] +pub struct FetchError { + pub message: String, +} + +impl FetchError { + pub fn new(err: anyhow::Error) -> Self { + Self { + message: format!("{}", err), + } + } } pub struct Gui { @@ -28,25 +44,30 @@ pub struct Gui { } impl Gui { - pub fn new() -> (Self, iced::Task) { - let tasks = vec![ - iced::Task::perform(fetch_buckets(), Message::RefetchedBuckets), - iced::Task::perform(fetch_accounts(), Message::RefetchedAccounts), - ]; + pub fn new(connection: &mut SqliteConnection) -> (Self, iced::Task) { let buckets = Vec::new(); let accounts = Vec::new(); - let gui = Self { + let mut gui = Self { active_screen: Screen::Main, buckets: buckets.clone(), main_screen: MainScreen::new(buckets, accounts), config: Config::default(), }; - (gui, iced::Task::batch(tasks)) + + let buckets = get_all_buckets(connection).map_err(FetchError::new); + let accounts = get_all_accounts(connection).map_err(FetchError::new); + + let tasks = iced::Task::batch(vec![ + gui.update(Message::RefetchedBuckets(buckets)), + gui.update(Message::RefetchedAccounts(accounts)), + ]); + + (gui, tasks) } } impl Gui { - pub fn view(&self) -> Element { + pub fn view<'a>(&'a self) -> Element<'a, Message> { match self.active_screen { Screen::Main => self.main_screen.view().map(Message::MainScreenMessage), } @@ -109,7 +130,7 @@ impl Gui { self.main_screen .update(main_screen::Message::SetBuckets(buckets)); } - Err(err) => println!("Failed to get buckets: {:?}", err), + Err(err) => println!("Failed to get buckets: {:?}", err.message), }; iced::Task::none() } @@ -119,7 +140,7 @@ impl Gui { self.main_screen .update(main_screen::Message::SetAccounts(accounts)); } - Err(err) => println!("Failed to get accounts: {:?}", err), + Err(err) => println!("Failed to get accounts: {:?}", err.message), } iced::Task::none() } diff --git a/schist_desktop_gui/src/gui/components/bucket_name_and_balance.rs b/schist_desktop_gui/src/gui/components/bucket_name_and_balance.rs index fa2dcac..b31e0e2 100644 --- a/schist_desktop_gui/src/gui/components/bucket_name_and_balance.rs +++ b/schist_desktop_gui/src/gui/components/bucket_name_and_balance.rs @@ -19,6 +19,10 @@ impl BucketNameAndBalance { impl<'a> Into>> for BucketNameAndBalance { fn into(self) -> Element<'a, navigation::Message> { - row![text(self.bucket.name.clone()), text(self.bucket.balance)].into() + row![ + text(self.bucket.name.clone()), + text(self.bucket.balance.unwrap_or(0)) + ] + .into() } } diff --git a/schist_desktop_gui/src/gui/components/transactions_view.rs b/schist_desktop_gui/src/gui/components/transactions_view.rs index 6181a14..3a8333c 100644 --- a/schist_desktop_gui/src/gui/components/transactions_view.rs +++ b/schist_desktop_gui/src/gui/components/transactions_view.rs @@ -43,7 +43,7 @@ impl TransactionsView { } impl<'a> Viewable<'a, Message> for TransactionsView { - fn view(&self) -> Element { + fn view(&'a self) -> Element<'a, Message> { let navigation = self.navigation.view().map(Message::NavigationMessage); let main_content = column![iced::widget::text(self.greeting.clone())]; row![ diff --git a/schist_desktop_gui/src/main.rs b/schist_desktop_gui/src/main.rs index 7e54ddb..24aedc0 100644 --- a/schist_desktop_gui/src/main.rs +++ b/schist_desktop_gui/src/main.rs @@ -1,5 +1,5 @@ mod config; -mod dummy_data; +mod connection; mod gui; mod settings; mod shortcut; @@ -8,17 +8,31 @@ mod theme; mod traits; mod window_settings; +use anyhow::Context; +use diesel_migrations::MigrationHarness; +use schist_schema::migrations::MIGRATIONS; + use crate::{ - config::Config, gui::Gui, settings::make_settings, theme::make_theme, + config::Config, + connection::establish_connection, + gui::Gui, + settings::make_settings, + theme::make_theme, window_settings::make_window_settings, }; -fn main() -> iced::Result { +fn main() -> anyhow::Result<()> { let config = Config::default(); - iced::application("Schist", Gui::update, Gui::view) - .settings(make_settings(&config.clone())) - .subscription(gui::subscription) - .theme(make_theme) - .window(make_window_settings(&config.clone())) - .run_with(Gui::new) + let mut connection = establish_connection() + .context("Failed to establish database connection")?; + connection.run_pending_migrations(MIGRATIONS) + .expect("Failed to run pending database migrations"); + +iced::application("Schist", Gui::update, Gui::view) + .settings(make_settings(&config.clone())) + .subscription(gui::subscription) + .theme(make_theme) + .window(make_window_settings(&config.clone())) + .run_with(move || Gui::new(&mut connection)) + .context("Failed to run Schist GUI") } -- cgit v1.2.3