From 65596363d43995bf2002d4aa8405d84484f2542b Mon Sep 17 00:00:00 2001 From: Joe Carstairs Date: Sat, 7 Feb 2026 18:45:08 +0000 Subject: task-088: cache open file --- Cargo.lock | 69 +++++++---- Cargo.toml | 2 + requirements/kanban.md | 1 - requirements/tasks/epic-000.md | 2 +- requirements/tasks/task-088.md | 2 + schist_desktop_gui/Cargo.toml | 3 + schist_desktop_gui/src/cache.rs | 83 +++++++++++++ schist_desktop_gui/src/dialog/new_file_dialog.rs | 6 +- schist_desktop_gui/src/dialog/pick_file_dialog.rs | 6 +- schist_desktop_gui/src/gui.rs | 128 ++++++++++++++++++--- schist_desktop_gui/src/gui/screens.rs | 2 +- schist_desktop_gui/src/gui/screens/files_screen.rs | 16 ++- .../screens/files_screen/update_files_screen.rs | 123 ++++---------------- schist_desktop_gui/src/main.rs | 7 +- 14 files changed, 292 insertions(+), 158 deletions(-) create mode 100644 schist_desktop_gui/src/cache.rs diff --git a/Cargo.lock b/Cargo.lock index 65ced99..77f25aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1659,9 +1659,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hassle-rs" @@ -2019,12 +2019,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.16.1", ] [[package]] @@ -3342,6 +3342,9 @@ dependencies = [ "schist_models", "schist_queries", "schist_schema", + "serde", + "toml", + "unwrap-infallible", ] [[package]] @@ -3426,18 +3429,28 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -3457,11 +3470,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -3861,14 +3874,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.9.2" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "indexmap", - "serde", + "serde_core", "serde_spanned", - "toml_datetime 0.7.0", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow", @@ -3882,11 +3895,11 @@ checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_datetime" -version = "0.7.0" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -3902,18 +3915,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.1" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.0.2" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tracing" @@ -4041,6 +4054,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unwrap-infallible" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e497bb1f828cc9fb236722c2eaa100dcf201563f38f4da6252357a59037adf31" + [[package]] name = "url" version = "2.5.6" @@ -4886,9 +4905,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index a763f80..66cee1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,5 @@ schist_queries = { path = "schist_core/schist_queries" } schist_schema = { path = "schist_core/schist_schema" } schist_traits = { path = "schist_core/schist_traits" } serde = { version = "1.0.209", features = ["derive"] } +toml = "0.9.11" +unwrap-infallible = "1.0.0" diff --git a/requirements/kanban.md b/requirements/kanban.md index a6bcbba..6ebb1c8 100644 --- a/requirements/kanban.md +++ b/requirements/kanban.md @@ -4,7 +4,6 @@ | -------- | -------- | -------- | | | | epic-000 | | ======== | ======== | ======== | -| | task-088 | | | | task-085 | | | | task-001 | | | | task-064 | | diff --git a/requirements/tasks/epic-000.md b/requirements/tasks/epic-000.md index 4180a60..00e3b92 100644 --- a/requirements/tasks/epic-000.md +++ b/requirements/tasks/epic-000.md @@ -12,7 +12,7 @@ Status: in progress - [x] task-012 (SQLite) - [x] task-072 (import from Actualbudget) - [x] task-089 (keyboard nav in files screen) -- [ ] task-088 (caching open file) +- [x] task-088 (caching open file) - [ ] task-085 (view transactions) - [ ] task-001 (inserting transactions) - [ ] task-064 (deleting transactions) diff --git a/requirements/tasks/task-088.md b/requirements/tasks/task-088.md index 0d0ed59..c729b28 100644 --- a/requirements/tasks/task-088.md +++ b/requirements/tasks/task-088.md @@ -3,6 +3,8 @@ Schist's desktop GUI should open the same file the user last had open without passing through the files screen. +Status: done + Source: task-072 Epic: epic-000 diff --git a/schist_desktop_gui/Cargo.toml b/schist_desktop_gui/Cargo.toml index 0ee5764..e5937d8 100644 --- a/schist_desktop_gui/Cargo.toml +++ b/schist_desktop_gui/Cargo.toml @@ -17,3 +17,6 @@ schist_fakes = { workspace = true } schist_models = { workspace = true } schist_queries = { workspace = true } schist_schema = { workspace = true } +serde = { workspace = true } +toml = { workspace = true } +unwrap-infallible = { workspace = true } diff --git a/schist_desktop_gui/src/cache.rs b/schist_desktop_gui/src/cache.rs new file mode 100644 index 0000000..51e2c55 --- /dev/null +++ b/schist_desktop_gui/src/cache.rs @@ -0,0 +1,83 @@ +use std::{ + fs::{create_dir_all, exists, read, write, File}, + path::{Path, PathBuf}, +}; + +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; + +const SCHIST_CACHE_DIR: &'static str = "schist"; +const SCHIST_CACHE_FILENAME: &'static str = "schist_cache.toml"; + +#[derive(Default, Deserialize, Serialize)] +pub struct Cache { + pub last_open_file: Option, +} + +pub struct CacheKeys { + pub last_open_file: &'static str, +} + +pub const CACHE_KEYS: CacheKeys = CacheKeys { + last_open_file: "last_open_file", +}; + +pub fn get_cache() -> Result { + const CONTEXT: &'static str = "Failed to get cache"; + let cache_filepath = get_and_touch_cache_filepath().context(CONTEXT)?; + let cache_toml = read(&cache_filepath).context(CONTEXT)?; + let cache_table: toml::Table = toml::from_slice(&cache_toml).context(CONTEXT)?; + Ok(Cache { + last_open_file: cache_table + .get(&CACHE_KEYS.last_open_file.to_string()) + .map(|o| o.as_str().map(str::to_string)) + .flatten(), + }) +} + +pub fn put_to_cache(key: &str, value: T) -> Result<()> +where + toml::Value: From, +{ + const CONTEXT: &'static str = "Failed to save cache"; + let cache_filepath = get_and_touch_cache_filepath().context(CONTEXT)?; + let curr_cache_toml = read(&cache_filepath).context(CONTEXT)?; + let mut cache: toml::Table = toml::from_slice(&curr_cache_toml).context(CONTEXT)?; + cache.insert(key.to_string(), value.into()); + let new_cache_toml = toml::to_string(&cache).context(CONTEXT)?; + write(cache_filepath, new_cache_toml).context(CONTEXT)?; + Ok(()) +} + +pub fn remove_from_cache(key: &str) -> Result<()> { + const CONTEXT: &'static str = "Failed to remove key from cache"; + let cache_filepath = get_and_touch_cache_filepath().context(CONTEXT)?; + let curr_cache_toml = read(&cache_filepath).context(CONTEXT)?; + let mut cache: toml::Table = toml::from_slice(&curr_cache_toml).context(CONTEXT)?; + cache.remove(key); + let new_cache_toml = toml::to_string(&cache).context(CONTEXT)?; + write(cache_filepath, new_cache_toml).context(CONTEXT)?; + Ok(()) +} + +fn get_and_touch_cache_filepath() -> Result { + let cache_dir = get_and_make_schist_cache_dir()?; + let cache_filepath = cache_dir.join(Path::new(SCHIST_CACHE_FILENAME)); + let does_cache_file_exist = exists(cache_filepath.clone()) + .context("Failed to determine the existence of Schist's cache file")?; + if !does_cache_file_exist { + File::create(cache_filepath.clone()).context("Failed to create a new cache file")?; + } + Ok(cache_filepath) +} + +fn get_and_make_schist_cache_dir() -> anyhow::Result { + let cache_dir = dirs::cache_dir().context("Failed to get app cache directory")?; + let schist_cache_dir = cache_dir.join(Path::new(SCHIST_CACHE_DIR)); + create_dir_all(&schist_cache_dir).context(format!( + "Failed to create app cache directory at {}", + schist_cache_dir.display() + ))?; + println!("Found app cache directory at {:?}", schist_cache_dir); + Ok(schist_cache_dir) +} diff --git a/schist_desktop_gui/src/dialog/new_file_dialog.rs b/schist_desktop_gui/src/dialog/new_file_dialog.rs index b950f68..0a148e1 100644 --- a/schist_desktop_gui/src/dialog/new_file_dialog.rs +++ b/schist_desktop_gui/src/dialog/new_file_dialog.rs @@ -1,6 +1,6 @@ -use crate::{database_connection::DatabaseConnectionResult, DatabaseConnection}; +use std::path::PathBuf; -pub fn new_file_dialog

(starting_directory: Option

) -> Option +pub fn new_file_dialog

(starting_directory: Option

) -> Option where P: AsRef, { @@ -11,5 +11,5 @@ where if let Some(starting_directory) = starting_directory { file_dialog = file_dialog.set_directory(starting_directory); } - file_dialog.save_file().map(DatabaseConnection::establish) + file_dialog.save_file() } diff --git a/schist_desktop_gui/src/dialog/pick_file_dialog.rs b/schist_desktop_gui/src/dialog/pick_file_dialog.rs index 53d36c6..fc1a330 100644 --- a/schist_desktop_gui/src/dialog/pick_file_dialog.rs +++ b/schist_desktop_gui/src/dialog/pick_file_dialog.rs @@ -1,6 +1,6 @@ -use crate::{database_connection::DatabaseConnectionResult, DatabaseConnection}; +use std::path::PathBuf; -pub fn pick_file_dialog

(starting_directory: Option

) -> Option +pub fn pick_file_dialog

(starting_directory: Option

) -> Option where P: AsRef, { @@ -10,5 +10,5 @@ where if let Some(starting_directory) = starting_directory { file_dialog = file_dialog.set_directory(starting_directory); } - file_dialog.pick_file().map(DatabaseConnection::establish) + file_dialog.pick_file() } diff --git a/schist_desktop_gui/src/gui.rs b/schist_desktop_gui/src/gui.rs index eb2fce9..26f147b 100644 --- a/schist_desktop_gui/src/gui.rs +++ b/schist_desktop_gui/src/gui.rs @@ -1,22 +1,33 @@ pub mod components; +use actualbudget_to_schist_transformer::schist_state::{export_schist_state, SchistState}; +use itertools::Itertools; pub mod screens; +use std::{path::PathBuf, str::FromStr}; + use diesel::SqliteConnection; -use iced::Element; +use iced::{Element, Task}; use schist_models::{Account, Bucket}; use schist_queries::{accounts::get_all_accounts, buckets::get_all_buckets}; use crate::{ + cache::{put_to_cache, remove_from_cache, CACHE_KEYS}, config::Config, + dialog::{import_from_actualbudget_dialog, new_file_dialog, pick_file_dialog}, gui::screens::{files_screen, main_screen, FilesScreen, MainScreen, Screen}, - paths::get_file_paths, + paths::{get_file_paths, get_schist_database_directory}, shortcut::KeyBind, traits::{Component, Viewable}, + Cache, DatabaseConnection, }; #[derive(Clone, Debug)] pub enum Message { Event(iced::Event), + FailedToCreateFile(String), + FailedToOpenFile(PathBuf, String), + FailedToPickFile(String), + FailedToImportFromActualbudget(String), FilesScreenMessage(files_screen::Message), MainScreenMessage(main_screen::Message), RefetchedAccounts(FetchResult>), @@ -47,16 +58,24 @@ pub struct Gui { } impl Gui { - pub fn new() -> (Self, iced::Task) { + pub fn new(cache: &Cache) -> (Self, iced::Task) { let file_paths = get_file_paths(); - let gui = Self { + let mut gui = Self { active_screen: Screen::FilesScreen, connection: None, files_screen: FilesScreen::new(&file_paths), main_screen: MainScreen::new(), config: Config::default(), }; - (gui, iced::Task::none()) + + let mut tasks = Vec::new(); + + if let Some(last_open_file) = &cache.last_open_file { + let Ok(last_open_file) = PathBuf::from_str(&last_open_file); + tasks.push(gui.open_file(&last_open_file)); + } + + (gui, Task::batch(tasks)) } } @@ -167,23 +186,102 @@ impl Gui { iced::Task::none() } Message::FilesScreenMessage(message) => self.update_files_screen(message), + Message::FailedToCreateFile(err) => { + self.update_files_screen(files_screen::Message::ReportFailedToCreateFile(err)) + } + Message::FailedToOpenFile(path, err) => { + self.update_files_screen(files_screen::Message::ReportFailedToOpenFile(path, err)) + } + Message::FailedToImportFromActualbudget(err) => self.update_files_screen( + files_screen::Message::ReportFailedToImportFromActualbudget(err), + ), + Message::FailedToPickFile(err) => { + self.update_files_screen(files_screen::Message::ReportFailedToPickFile(err)) + } } } fn update_files_screen(&mut self, message: files_screen::Message) -> iced::Task { match self.files_screen.update(message) { + files_screen::Action::ImportFromActualbudget => self.import_from_actualbudget(), + files_screen::Action::NewFile => self.new_file(), files_screen::Action::None => iced::Task::none(), - files_screen::Action::OpenedFile(sqlite_connection) => { - let mut connection = sqlite_connection.connection; - let buckets = get_all_buckets(&mut connection).map_err(FetchError::new); - let accounts = get_all_accounts(&mut connection).map_err(FetchError::new); - self.connection = Some(connection); - self.active_screen = Screen::MainScreen; - iced::Task::batch(vec![ - self.update(Message::RefetchedBuckets(buckets)), - self.update(Message::RefetchedAccounts(accounts)), - ]) + files_screen::Action::OpenFile(path) => self.open_file(&path), + files_screen::Action::PickFile => self.pick_file(), + } + } + + fn open_file(&mut self, file_path: &PathBuf) -> Task { + let sqlite_connection = DatabaseConnection::establish(file_path.to_path_buf()).ok(); + if let Err((path, err)) = sqlite_connection { + return Task::done(Message::FailedToOpenFile(path, err.chain().join("\n | "))); + } + self.on_new_connection(sqlite_connection.unwrap().connection, file_path) + } + + fn on_new_connection( + &mut self, + mut connection: SqliteConnection, + path: &PathBuf, + ) -> Task { + let buckets = get_all_buckets(&mut connection).map_err(FetchError::new); + let accounts = get_all_accounts(&mut connection).map_err(FetchError::new); + self.connection = Some(connection); + self.active_screen = Screen::MainScreen; + if let Some(path) = path.to_str() { + let _ = put_to_cache(CACHE_KEYS.last_open_file, path); + } else { + let _ = remove_from_cache(CACHE_KEYS.last_open_file); + } + iced::Task::batch(vec![ + self.update(Message::RefetchedBuckets(buckets)), + self.update(Message::RefetchedAccounts(accounts)), + ]) + } + + fn new_file(&mut self) -> Task { + match new_file_dialog(get_schist_database_directory().ok()) { + Some(path) => self.open_file(&path), + None => Task::none(), + } + } + + fn new_file_with_state(&mut self, state: &SchistState) -> Task { + match new_file_dialog(get_schist_database_directory().ok()) { + Some(path) => { + let sqlite_connection = DatabaseConnection::establish(path.clone()).ok(); + if let Err((path, err)) = sqlite_connection { + return Task::done(Message::FailedToOpenFile(path, err.chain().join("\n | "))); + } + let mut db_conn = sqlite_connection.unwrap().connection; + export_schist_state(state, &mut db_conn).map_or_else( + |err| Task::done(Message::FailedToCreateFile(err.chain().join("\n | "))), + move |()| self.on_new_connection(db_conn, &path), + ) } + None => Task::none(), + } + } + + fn import_from_actualbudget(&mut self) -> Task { + match import_from_actualbudget_dialog() { + Some(Ok(state)) => self.new_file_with_state(&state), + Some(Err((_path, err))) => Task::done(Message::FailedToImportFromActualbudget( + err.chain().join("\n | "), + )), + None => Task::none(), + } + } + + fn pick_file(&mut self) -> Task { + match pick_file_dialog(get_schist_database_directory().ok()) { + Some(path) => match DatabaseConnection::establish(path.clone()).ok() { + Ok(db_conn) => self.on_new_connection(db_conn.connection, &path), + Err((_path, err)) => { + Task::done(Message::FailedToPickFile(err.chain().join("\n | "))) + } + }, + None => Task::none(), } } } diff --git a/schist_desktop_gui/src/gui/screens.rs b/schist_desktop_gui/src/gui/screens.rs index abca2b6..5acf73b 100644 --- a/schist_desktop_gui/src/gui/screens.rs +++ b/schist_desktop_gui/src/gui/screens.rs @@ -4,7 +4,7 @@ pub mod main_screen; pub use files_screen::FilesScreen; pub use main_screen::MainScreen; -#[derive(Default)] +#[derive(Clone, Default)] pub enum Screen { #[default] FilesScreen, diff --git a/schist_desktop_gui/src/gui/screens/files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen.rs index 9757355..9700e7a 100644 --- a/schist_desktop_gui/src/gui/screens/files_screen.rs +++ b/schist_desktop_gui/src/gui/screens/files_screen.rs @@ -3,13 +3,10 @@ mod view_files_screen; use std::{fmt::Debug, path::PathBuf}; -use crate::{ - gui::components::{navigation, Navigation, Panel}, - DatabaseConnection, -}; +use crate::gui::components::{navigation, Navigation, Panel}; pub struct FilesScreen { - options: Navigation>, + pub options: Navigation>, } #[derive(Clone, Debug, PartialEq)] @@ -23,6 +20,10 @@ pub enum OptionId { #[derive(Clone, Debug, PartialEq)] pub enum Message { NavigationMessage(navigation::Message>), + ReportFailedToCreateFile(String), + ReportFailedToImportFromActualbudget(String), + ReportFailedToPickFile(String), + ReportFailedToOpenFile(PathBuf, String), ActivateSelectedOption, NextOption, PrevOption, @@ -30,8 +31,11 @@ pub enum Message { #[derive(Debug)] pub enum Action { + ImportFromActualbudget, + NewFile, None, - OpenedFile(DatabaseConnection), + OpenFile(PathBuf), + PickFile, } impl<'a> FilesScreen { diff --git a/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs index aa47f56..1339138 100644 --- a/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs +++ b/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs @@ -1,13 +1,6 @@ -use actualbudget_to_schist_transformer::schist_state::{export_schist_state, SchistState}; -use itertools::Itertools; - use crate::{ - database_connection::DatabaseConnectionResult, - dialog::{import_from_actualbudget_dialog, new_file_dialog, pick_file_dialog}, gui::components::{navigation, Panel}, - paths::get_schist_database_directory, traits::Component, - DatabaseConnection, }; use super::{Action, FilesScreen, Message, OptionId}; @@ -24,92 +17,31 @@ impl<'a> Component<'a, Message, Action> for FilesScreen { } None => Action::None, }, - } - } -} - -impl FilesScreen { - fn pick_file( - &mut self, - ) -> Option> { - pick_file_dialog(get_schist_database_directory().ok()).map(|db_connection_result| { - db_connection_result.ok().inspect_err(|(_path, err)| { - self.report_err( - super::OptionId::PickFile, - format!("{}", err.chain().join("\n | ")).as_str(), - "Failed to open file", - ); - }) - }) - } - - fn open_file( - &mut self, - file_path: std::path::PathBuf, - ) -> Result { - DatabaseConnection::establish(file_path.clone()) - .ok() - .inspect_err(|(_path, err)| { - self.report_err( - OptionId::OpenFile(file_path), - format!("{}", err.chain().join("\n | ")).as_str(), - "Failed to open file", - ); - }) - } - - fn new_file( - &mut self, - ) -> Option> { - new_file_dialog(get_schist_database_directory().ok()).map(|result| { - result.ok().inspect_err(|(_path, err)| { - self.report_err( - OptionId::NewFile, - "Failed to create new file", - format!("{}", err.chain().join("\n | ")).as_str(), - ); - }) - }) - } - - fn import_from_actualbudget(&mut self) -> Option> { - match import_from_actualbudget_dialog() { - Some(Ok(state)) => self.new_file_with_state(&state), - Some(Err((_path, err))) => Some({ + Message::ReportFailedToOpenFile(path, err) => { + self.report_err(OptionId::OpenFile(path), &err, "Failed to open file"); + Action::None + } + Message::ReportFailedToCreateFile(err) => { + self.report_err(OptionId::NewFile, &err, "Failed to create new file"); + Action::None + } + Message::ReportFailedToImportFromActualbudget(err) => { self.report_err( OptionId::ImportFromActualbudget, + &err, "Failed to import from Actualbudget", - format!("{}", err.chain().join("\n | ")).as_str(), - ); - Err(err) - }), - None => None, - } - } - - fn new_file_with_state( - &mut self, - state: &SchistState, - ) -> Option> { - match new_file_dialog(get_schist_database_directory().ok()) - .map(DatabaseConnectionResult::ok) - { - Some(Ok(mut file)) => Some({ - export_schist_state(state, &mut file.connection).ok()?; - Ok(file) - }), - Some(Err((_path, err))) => Some({ - self.report_err( - OptionId::NewFile, - format!("{}", err.chain().join("\n | ")).as_str(), - "Failed to create new file", ); - Err(err) - }), - None => None, + Action::None + } + Message::ReportFailedToPickFile(err) => { + self.report_err(OptionId::PickFile, &err, "Failed to choose file"); + Action::None + } } } +} +impl FilesScreen { fn report_err(&mut self, id: OptionId, err: &str, dialog_title: &str) { let old_panel = self.options.find(|o| o.id == id); let file_panel_with_err = Panel::new( @@ -134,33 +66,22 @@ impl FilesScreen { Panel { id: OptionId::NewFile, .. - } => match self.new_file() { - Some(Ok(file)) => Action::OpenedFile(file), - Some(Err(_)) | None => Action::None, - }, + } => Action::NewFile, Panel { id: OptionId::OpenFile(file_path), .. - } => self - .open_file(file_path.clone()) - .map_or(Action::None, Action::OpenedFile), + } => Action::OpenFile(file_path), Panel { id: OptionId::PickFile, .. - } => match self.pick_file() { - Some(Ok(file)) => Action::OpenedFile(file), - Some(Err(_)) | None => Action::None, - }, + } => Action::PickFile, Panel { id: OptionId::ImportFromActualbudget, .. - } => match self.import_from_actualbudget() { - Some(Ok(file)) => Action::OpenedFile(file), - Some(Err(_)) | None => Action::None, - }, + } => Action::ImportFromActualbudget, }, navigation::Action::SelectOption(_) | navigation::Action::None => Action::None, } diff --git a/schist_desktop_gui/src/main.rs b/schist_desktop_gui/src/main.rs index 6dd2622..a5749d3 100644 --- a/schist_desktop_gui/src/main.rs +++ b/schist_desktop_gui/src/main.rs @@ -1,3 +1,4 @@ +mod cache; mod config; mod database_connection; mod dialog; @@ -10,22 +11,24 @@ mod theme; mod traits; mod window_settings; +pub use cache::Cache; pub use database_connection::DatabaseConnection; use anyhow::Context; use crate::{ - config::Config, gui::Gui, settings::make_settings, theme::make_theme, + cache::get_cache, config::Config, gui::Gui, settings::make_settings, theme::make_theme, window_settings::make_window_settings, }; fn main() -> anyhow::Result<()> { + let cache = get_cache().unwrap_or_else(|_| Cache::default()); 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) + .run_with(move || Gui::new(&cache)) .context("Failed to run Schist GUI") } -- cgit v1.2.3