diff --git a/src/app.rs b/src/app.rs index eaafa91..d565458 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,9 +1,10 @@ +use std::sync::Arc; use crate::data::Id; use crate::data::entities::DeckSettings; use crate::data::entities::{WordData, WordGroup}; use crate::data_provider::decks::load_sets; use crate::data_provider::settings::get_setting; -use crate::data_provider::sqlite::default_connection; +use crate::data_provider::sqlite::{SqliteConnection}; use crate::data_provider::words::{load_word_groups, load_words}; use crate::navigation::RootMessage::Keyboard; use crate::navigation::{AppSettings, RootMessage, ScreenState}; @@ -15,6 +16,7 @@ use iced_core::window::Position; use iced_core::window::settings::PlatformSpecific; use iced_core::{Font, Size, Theme}; use log::info; +use parking_lot::RwLock; use rusqlite::Connection; pub const USER_FONT: Font = Font::with_name("Noto Sans JP"); @@ -57,12 +59,13 @@ fn subscription(_state: &ScreenState) -> Subscription { keyboard::listen().map(Keyboard) } +#[derive(Clone)] pub struct AppState { pub dictionary: Vec, pub decks: Vec, pub word_groups: Vec, - pub connection: Connection, - pub sync_data: AppSettings, + pub connection: Arc>, + pub sync_data: Arc>, pub activity: HashMap>, } @@ -78,26 +81,27 @@ impl AppState { dictionary: vec![], decks: vec![], word_groups: vec![], - connection: default_connection(), - sync_data: AppSettings { + connection: Arc::new(RwLock::new(Default::default())), + sync_data: Arc::new(RwLock::new(AppSettings { key: None, auto_web_fetch: false, - }, + })), activity: Default::default(), } } } pub fn fill_state(state: &mut AppState) { - let list = load_words(&state.connection); - let sets = load_sets(&state.connection); - let groups = load_word_groups(&state.connection); - let setting = load_settings(&state.connection); + let connection = state.connection.read(); + let list = load_words(&connection); + let sets = load_sets(&connection); + let groups = load_word_groups(&connection); + let setting = load_settings(&connection); state.dictionary = list.unwrap_or_default(); state.decks = sets.unwrap_or_default(); state.word_groups = groups.unwrap_or_default(); - state.sync_data = setting + state.sync_data = Arc::new(RwLock::new(setting)) } fn load_settings(connection: &Connection) -> AppSettings { diff --git a/src/core/deck.rs b/src/core/deck.rs index b09e41c..d5aca96 100644 --- a/src/core/deck.rs +++ b/src/core/deck.rs @@ -9,15 +9,13 @@ use crate::data_provider::card_states::{load_stats_of_deck, update_stat_score}; use crate::data_provider::history::push_note; use crate::notification::{MaybeError, NotificationData}; use chrono::Utc; -use parking_lot::Mutex; -use std::sync::Arc; #[derive(Clone)] pub struct DeckData { pub(crate) words: Vec, pub(crate) set: Vec, current_word_index: Option, - state: Arc>, + state: AppState, order_module: OrderModule, open_mode: OrderMode, settings: DeckSettings, @@ -25,21 +23,20 @@ pub struct DeckData { impl DeckData { pub fn new( settings: &DeckSettings, - state: Arc>, + state: AppState, ) -> Result { let state_for = state.clone(); - let state_locked = state.lock(); - let stats = load_stats_of_deck(settings, &state_locked.connection)?; + let stats = load_stats_of_deck(settings, &state.connection.read())?; let words = stats .iter() .map(|stat| { - state_locked + state .dictionary .binary_search_by_key(&stat.word_id, |w| w.id) .expect("No word found in dictionary") }) - .map(|index| &state_locked.dictionary[index]) + .map(|index| &state.dictionary[index]) .cloned() .collect(); @@ -117,7 +114,7 @@ impl DeckData { OrderMode::FullRandom => RandomSRS(RandomSRSModule::new()), } } - update_stat_score(word, &self.state.lock().connection)?; + update_stat_score(word, &self.state.connection.read())?; let _ = push_note( self.settings.id, HistoryItem { diff --git a/src/data_provider/sqlite.rs b/src/data_provider/sqlite.rs index 85821a7..2021209 100644 --- a/src/data_provider/sqlite.rs +++ b/src/data_provider/sqlite.rs @@ -1,5 +1,6 @@ use crate::fs::*; use rusqlite::Connection; +use std::ops::{Deref, DerefMut}; pub fn create_db() { let db_file = db_path(); @@ -84,3 +85,29 @@ pub fn default_connection() -> Connection { .expect("Failed to set PRAGMA"); connection } + +pub struct SqliteConnection(Option); +impl Deref for SqliteConnection { + type Target = Connection; + fn deref(&self) -> &Self::Target { + self.0.as_ref().unwrap() + } +} + +impl DerefMut for SqliteConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0.as_mut().unwrap() + } +} + +impl Default for SqliteConnection { + fn default() -> Self { + SqliteConnection(Some(default_connection())) + } +} + +impl SqliteConnection { + pub fn finish(&mut self) { + self.0 = None; + } +} diff --git a/src/navigation.rs b/src/navigation.rs index ecd87f8..acd7636 100644 --- a/src/navigation.rs +++ b/src/navigation.rs @@ -1,7 +1,7 @@ use crate::app::{AppState, fill_state}; use crate::data::Id; use crate::data_provider::history::get_history_of_set; -use crate::data_provider::sqlite::{create_db, default_connection}; +use crate::data_provider::sqlite::{create_db, default_connection, SqliteConnection}; use crate::data_provider::web_api::{ get_local_version, get_web_version, load_data, set_local_version, }; @@ -47,7 +47,6 @@ impl Default for ScreenState { fn default() -> Self { let mut state = AppState::new(); fill_state(&mut state); - let state = Arc::new(Mutex::new(state)); ScreenState { stack: vec![Selector(SelectorState::new(state.clone()))], app_state: state.clone(), @@ -99,7 +98,7 @@ pub enum Page { pub struct ScreenState { stack: Vec, - app_state: Arc>, + app_state: AppState, notification_manager: NotificationManager, } @@ -114,9 +113,9 @@ impl ScreenState { }); let mut final_task = reading_additional_task; - let state_guard = state.app_state.lock(); - if state_guard.sync_data.auto_web_fetch - && let Some(key) = state_guard.sync_data.key.clone() + let state_guard = &state.app_state; + if state_guard.sync_data.read().auto_web_fetch + && let Some(key) = state_guard.sync_data.read().key.clone() { final_task = Task::batch([ final_task, @@ -133,7 +132,6 @@ impl ScreenState { ]); } - drop(state_guard); info!("Boot finished"); (state, final_task) } @@ -192,15 +190,15 @@ impl ScreenState { } if let DataLoaded(data) = message { - self.app_state.lock().activity = data; + self.app_state.activity = data; return Task::none(); } if let UpdateData = message { info!("Loading data"); - let mut state = self.app_state.lock(); + let state = &mut self.app_state; if cfg!(windows) { - state.connection = Connection::open_in_memory().unwrap(); + state.connection.write().finish(); } let db_file = db_path(); let temp_db_file = temp_db_path(); @@ -208,8 +206,8 @@ impl ScreenState { return push_error_task!(PermissionDenied); } - state.connection = default_connection(); - fill_state(&mut state); + state.connection = Default::default(); + fill_state(state); return Task::none(); } diff --git a/src/pages/dictionary.rs b/src/pages/dictionary.rs index b800564..16bd9f3 100644 --- a/src/pages/dictionary.rs +++ b/src/pages/dictionary.rs @@ -30,7 +30,7 @@ use std::time::{Duration, Instant}; #[derive(Clone)] pub struct DictionaryState { - state: Arc>, + state: AppState, include_map: Vec, tag_map: HashMap, reverse: bool, @@ -300,7 +300,7 @@ impl NavigatedPage for DictionaryState { } impl DictionaryState { - pub fn new(state: Arc>) -> Self { + pub fn new(state: AppState) -> Self { let len = state.lock().dictionary.len(); let mut result = DictionaryState { diff --git a/src/pages/history.rs b/src/pages/history.rs index 4cc0558..3770c21 100644 --- a/src/pages/history.rs +++ b/src/pages/history.rs @@ -48,7 +48,7 @@ impl NavigatedPage for HistoryState { } impl HistoryState { - pub fn new(id: crate::data::Id, state: Arc>) -> Self { + pub fn new(id: crate::data::Id, state: AppState) -> Self { let state = state.lock(); let history = get_history_of_set(id).unwrap_or_default(); let words = history diff --git a/src/pages/import.rs b/src/pages/import.rs index 5a4de5d..1c18ab5 100644 --- a/src/pages/import.rs +++ b/src/pages/import.rs @@ -34,7 +34,7 @@ const DEFAULT_FIELDS: [&str; 6] = ["key", "value", "tags", "reading", "context", #[derive(Clone)] pub struct ImportState { - state: Arc>, + state: AppState, path: Option, import_data: Option, selected_index: usize, @@ -318,7 +318,7 @@ impl ImportState { } impl ImportState { - pub fn new(state: Arc>) -> ImportState { + pub fn new(state: AppState) -> ImportState { ImportState { state, path: None, diff --git a/src/pages/repetition.rs b/src/pages/repetition.rs index cbf5d91..e40e5f9 100644 --- a/src/pages/repetition.rs +++ b/src/pages/repetition.rs @@ -22,7 +22,7 @@ use std::sync::Arc; use tokio::task::spawn_blocking; pub struct RepetitionState { - state: Arc>, + state: AppState, pub settings: DeckSettings, pub set: DeckData, current_word: WordData, @@ -104,7 +104,7 @@ impl NavigatedPage for RepetitionState { impl RepetitionState { pub(crate) fn new( set: DeckSettings, - state: Arc>, + state: AppState, ) -> Result { let mut card_set = DeckData::new(&set, state.clone())?; let (word, stat) = card_set.next(); diff --git a/src/pages/repetition_settings.rs b/src/pages/repetition_settings.rs index cf801bb..a48cca0 100644 --- a/src/pages/repetition_settings.rs +++ b/src/pages/repetition_settings.rs @@ -21,14 +21,14 @@ use std::time::Duration; pub struct RepetitionSettingsState { set: DeckSettings, index: usize, - state: Arc>, + state: AppState, real_delete: bool, correct_filter: bool, filter_changed: bool, } impl RepetitionSettingsState { - pub(crate) fn new(index: usize, state: Arc>) -> RepetitionSettingsState { + pub(crate) fn new(index: usize, state: AppState) -> RepetitionSettingsState { let set; { let local_state = state.lock(); diff --git a/src/pages/repetitions.rs b/src/pages/repetitions.rs index 38f44d2..7179ca7 100644 --- a/src/pages/repetitions.rs +++ b/src/pages/repetitions.rs @@ -36,7 +36,7 @@ pub struct RepetitionsState { word_id_index_map: HashMap, selected_deck_index: Option, decks: Vec, - state: Arc>, + state: AppState, } impl NavigatedPage for RepetitionsState { @@ -257,7 +257,7 @@ impl NavigatedPage for RepetitionsState { } impl RepetitionsState { - pub(crate) fn new(state: Arc>) -> RepetitionsState { + pub(crate) fn new(state: AppState) -> RepetitionsState { let state_ = state.lock(); let mut map = HashMap::with_capacity(state_.dictionary.len()); diff --git a/src/pages/selector.rs b/src/pages/selector.rs index f641efa..b8d1cca 100644 --- a/src/pages/selector.rs +++ b/src/pages/selector.rs @@ -22,7 +22,7 @@ use std::sync::Arc; pub struct SelectorState { pub set: KanaSet, is_writing: bool, - state: Arc>, + state: AppState, } #[derive(Debug, Clone)] @@ -114,7 +114,7 @@ impl NavigatedPage for SelectorState { } impl SelectorState { - pub fn new(state: Arc>) -> Self { + pub fn new(state: AppState) -> Self { Self { set: Default::default(), is_writing: false, diff --git a/src/pages/sync.rs b/src/pages/sync.rs index e9f7900..d5cf083 100644 --- a/src/pages/sync.rs +++ b/src/pages/sync.rs @@ -34,7 +34,7 @@ pub enum SyncMessage { } #[derive(Clone)] pub struct SyncState { - state: Arc>, + state: AppState, auto_fetch: bool, frozen: bool, progress: f32, @@ -177,7 +177,7 @@ impl NavigatedPage for SyncState { } impl SyncState { - pub fn new(state: Arc>) -> SyncState { + pub fn new(state: AppState) -> SyncState { let auto_fetch; { auto_fetch = state.lock().sync_data.auto_web_fetch; diff --git a/src/pages/word.rs b/src/pages/word.rs index fa95d58..3ff5300 100644 --- a/src/pages/word.rs +++ b/src/pages/word.rs @@ -15,7 +15,7 @@ use std::sync::Arc; #[derive(Clone)] pub struct WordState { - state: Arc>, + state: AppState, index: usize, word: WordData, } @@ -136,7 +136,7 @@ impl NavigatedPage for WordState { } impl WordState { - pub(crate) fn new(word: WordData, index: usize, state: Arc>) -> WordState { + pub(crate) fn new(word: WordData, index: usize, state: AppState) -> WordState { WordState { state, index, word } } }