diff --git a/src/data_provider/history.rs b/src/data_provider/history.rs index ff98019..c85e04e 100644 --- a/src/data_provider/history.rs +++ b/src/data_provider/history.rs @@ -32,7 +32,7 @@ fn parse_history_items(strings: Vec) -> Vec { if let [time, word, mode, before, after] = string.split(';').collect::>()[..] { items.push(HistoryItem { timestamp: DateTime::from_timestamp(time.parse().unwrap(), 0).unwrap(), - word_id: word.parse::().unwrap().into(), + word_id: word.parse::().unwrap().into(), mode: match mode.parse::().unwrap() { 2 => WordOpenMode::Hard, 3 => WordOpenMode::Ok, diff --git a/src/lang.rs b/src/lang.rs index 78d3da7..5fe2b2e 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -1,9 +1,9 @@ -use crate::data_provider::card_stats::{delete_stat, load_stats_of_set, update_stat_score}; -use crate::data_provider::history::{push_note, HistoryItem}; use crate::AppState; +use crate::data_provider::card_stats::{delete_stat, load_stats_of_set, update_stat_score}; +use crate::data_provider::history::{HistoryItem, push_note}; use chrono::{DateTime, Utc}; -use rand::distr::weighted::WeightedIndex; use rand::distr::Distribution; +use rand::distr::weighted::WeightedIndex; use rand::prelude::SliceRandom; use rand::rng; use rand::rngs::ThreadRng; @@ -11,11 +11,13 @@ use rayon::iter::IndexedParallelIterator; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use rhai::{Engine, Scope}; -use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef}; use rusqlite::ToSql; +use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef}; use std::cmp::PartialEq; use std::collections::HashMap; use std::fmt::{Display, Formatter}; +use std::num::ParseIntError; +use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::Instant; @@ -26,6 +28,47 @@ const FADE_PER_DAY: f32 = 0.95; #[derive(Clone, PartialEq, Eq, Debug, Hash, Copy)] pub(crate) struct Id(u32); pub const INVALID_ID: Id = Id(0); + +impl FromStr for Id { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + Ok(Id(s.parse::()?)) + } +} + +impl Into for u32 { + fn into(self) -> Id { + Id(self) + } +} + +impl Into for Id { + fn into(self) -> u32 { + self.0 + } +} +impl Display for Id { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl FromSql for Id { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + match value { + ValueRef::Integer(i) => Ok(Id(i as u32)), + _ => Err(FromSqlError::InvalidType), + } + } +} + +impl ToSql for Id { + fn to_sql(&self) -> rusqlite::Result> { + Ok(ToSqlOutput::from(self.0 as i64)) + } +} + #[derive(Clone, Debug)] pub struct KanaSet { name: String, @@ -33,13 +76,11 @@ pub struct KanaSet { pub(crate) dictionary: Vec>, pub(crate) include_map: [bool; 10], } - #[derive(Clone, Debug)] pub enum KanaType { Hiragana, Katakana, } - impl KanaSet { pub fn hiragana() -> Self { Self { @@ -219,7 +260,6 @@ impl KanaSet { current_set } } - impl Default for KanaSet { fn default() -> Self { KanaSet::hiragana() @@ -230,7 +270,6 @@ impl PartialEq for KanaSet { self.name == other.name } } - #[derive(Clone, Debug)] pub struct WordData { pub id: Id, @@ -240,11 +279,10 @@ pub struct WordData { pub additional: HashMap, pub group_id: Id, } - impl WordData { pub fn new() -> Self { Self { - id: 0.into(), + id: INVALID_ID, key: String::new(), value: String::new(), tags: String::new(), @@ -253,13 +291,11 @@ impl WordData { } } } - #[derive(Clone)] pub struct WordGroup { pub id: Id, pub name: String, } - #[derive(Clone, PartialEq)] pub struct CardStatistics { pub id: Id, @@ -268,7 +304,6 @@ pub struct CardStatistics { pub last_open: DateTime, pub score: u8, } - impl CardStatistics { pub fn update(&mut self, status: WordOpenMode) { match status { @@ -295,7 +330,6 @@ impl CardStatistics { (self.score as f32 * multiplier).max(1.0) } } - #[derive(Clone, Copy)] pub enum WordOpenMode { Easy, @@ -303,7 +337,6 @@ pub enum WordOpenMode { Hard, None, } - #[derive(Clone)] pub struct DeckData { words: Vec, @@ -313,7 +346,6 @@ pub struct DeckData { order_module: OrderModule, settings: DeckSettings, } - impl DeckData { pub fn new(settings: &DeckSettings, state: Arc>) -> Self { let state_for = state.clone(); @@ -353,7 +385,6 @@ impl DeckData { settings: settings.clone(), } } - pub fn next(&mut self) -> (WordData, CardStatistics) { let index = match self.order_module.clone() { OrderModule::SemiRandomSRS(mut module) => { @@ -385,7 +416,6 @@ impl DeckData { self.current_word_index = Some(index); (self.words[index].clone(), self.set[index].clone()) } - pub fn open(&mut self, status: WordOpenMode) { if self.current_word_index.is_none() { return; @@ -423,45 +453,38 @@ impl DeckData { }, ) } - pub fn len(&self) -> usize { self.set.len() } } - #[derive(Clone, PartialEq, Copy, Eq)] pub enum OrderMode { Default, TrainWorstFirst, FullRandom, } - #[derive(Clone, Copy, Eq, PartialEq, Default)] pub enum AppendMode { Full, #[default] Manual, } - #[derive(Clone)] enum OrderModule { SemiRandomSRS(SemiRandomSRSModule), RandomSRS(RandomSRSModule), WorstWordsSRS(WorstWordsSRSModule), } - trait SRSModule { fn next(&mut self, set: &mut DeckData) -> usize; fn open(&mut self, status: WordOpenMode, index: usize, updated_word: CardStatistics); fn init(&mut self, set: &mut DeckData); } - #[derive(Clone)] struct RandomSRSModule { basket: Vec, initialized: bool, } - impl RandomSRSModule { fn new() -> RandomSRSModule { Self { @@ -470,7 +493,6 @@ impl RandomSRSModule { } } } - impl SRSModule for RandomSRSModule { fn next(&mut self, set: &mut DeckData) -> usize { if self.basket.is_empty() { @@ -489,7 +511,6 @@ impl SRSModule for RandomSRSModule { self.basket.shuffle(&mut rand::rng()) } } - #[derive(Clone)] struct SemiRandomSRSModule { history: Vec, @@ -497,7 +518,6 @@ struct SemiRandomSRSModule { generator: ThreadRng, initialized: bool, } - impl SemiRandomSRSModule { fn new() -> SemiRandomSRSModule { SemiRandomSRSModule { @@ -508,7 +528,6 @@ impl SemiRandomSRSModule { } } } - impl SRSModule for SemiRandomSRSModule { fn next(&mut self, set: &mut DeckData) -> usize { let index = self.last_weights.sample(&mut self.generator); @@ -524,14 +543,12 @@ impl SRSModule for SemiRandomSRSModule { index } - fn open(&mut self, _: WordOpenMode, index: usize, word: CardStatistics) { let new_weight = (100.0 / word.calculated_score()).powf(2.0); self.last_weights .update_weights(&[(index, &new_weight)]) .unwrap(); } - fn init(&mut self, set: &mut DeckData) { self.initialized = true; let weights = set @@ -542,13 +559,11 @@ impl SRSModule for SemiRandomSRSModule { self.last_weights = WeightedIndex::new(weights).unwrap(); } } - impl SemiRandomSRSModule { fn history_len(&self, set: &DeckData) -> usize { ((set.len() as f32 * MAX_HISTORY_LEN_PART) as usize).clamp(1, MAX_HISTORY_LEN) } } - #[derive(Clone)] struct WorstWordsSRSModule { initialized: bool, @@ -558,7 +573,6 @@ struct WorstWordsSRSModule { queue: Vec, rounds_count: u8, } - impl SRSModule for WorstWordsSRSModule { fn next(&mut self, set: &mut DeckData) -> usize { if self.rounds_remaining == 0 { @@ -580,7 +594,6 @@ impl SRSModule for WorstWordsSRSModule { self.initialized = true; } } - impl WorstWordsSRSModule { fn new() -> WorstWordsSRSModule { WorstWordsSRSModule { @@ -592,7 +605,6 @@ impl WorstWordsSRSModule { queue: vec![], } } - fn fill_pool(&mut self, set: &DeckData) { let mut sorted = set .set @@ -609,7 +621,6 @@ impl WorstWordsSRSModule { self.pool = worst; } } - #[derive(Clone)] pub struct DeckSettings { pub id: Id, @@ -621,7 +632,6 @@ pub struct DeckSettings { pub worst_words_list: Option>, pub open_mode: OrderMode, } - impl DeckSettings { pub(crate) fn with_name(name: String) -> DeckSettings { DeckSettings { @@ -635,13 +645,11 @@ impl DeckSettings { open_mode: OrderMode::Default, } } - pub(crate) fn check_filter(&self) -> bool { let engine = Engine::new(); let ast = engine.compile(&self.filter); ast.is_ok() } - pub fn get_word_list(&self, state: &AppState) -> Vec { let time = Instant::now(); let mut list = vec![]; @@ -692,46 +700,7 @@ impl DeckSettings { list } - pub fn require_speech(&self) -> bool { self.forward == "speech" || self.backward == "speech" } -} - -impl Into for u32{ - fn into(self) -> Id { - Id(self) - } -} - -// impl From for Id{ -// fn from(id: u32) -> Id{ -// Id(id) -// } -// } - -impl Into for Id{ - fn into(self) -> u32 { - self.0 - } -} -impl Display for Id { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl FromSql for Id{ - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - match value { - ValueRef::Integer(i) => Ok(Id(i as u32)), - _ => Err(FromSqlError::InvalidType), - } - } -} - -impl ToSql for Id{ - fn to_sql(&self) -> rusqlite::Result> { - Ok(ToSqlOutput::from(self.0 as i64)) - } } \ No newline at end of file diff --git a/src/navigation.rs b/src/navigation.rs index 1e3e7b1..bf2c459 100644 --- a/src/navigation.rs +++ b/src/navigation.rs @@ -128,8 +128,8 @@ impl ScreenState { let mut vec = vec![]; let history_file_name = file.file_name().into_string().unwrap(); let id : Id = history_file_name[4..history_file_name.len() - 12] - .parse::() - .unwrap().into(); + .parse::() + .unwrap(); let history = get_history_of_set(id); let by_date = history.chunk_by(|x, x1| { diff --git a/src/repetitions.rs b/src/repetitions.rs index d57f249..a5e1fa0 100644 --- a/src/repetitions.rs +++ b/src/repetitions.rs @@ -27,11 +27,9 @@ use std::sync::{Arc, Mutex}; #[derive(Clone)] pub struct RepetitionsState { - correct_filters: Vec, - current_sets_cards_cache: HashMap, Vec)>, word_id_index_map: HashMap, - local_settings: HashMap, - view_data: Option, + view_data: Option, + decks: Vec, state: Arc>, } @@ -489,20 +487,17 @@ pub enum RepetitionsMessage { AppendWords(usize), } -#[derive(Clone, Default)] -struct DeckLocalSetting { +#[derive(Clone)] +struct DeckViewData { + set_settings: DeckSettings, + existing_words_indices: Option>, + available_words_indices: Option>, append_mode: AppendMode, append_warning: bool, + valid_filter: bool, + index: usize, } - -#[derive(Clone)] -struct SetViewData{ - set_local_settings: DeckLocalSetting, - set_settings: DeckSettings, - existing_words_indices: Vec, - available_words_indices: Vec, -} -impl Deref for SetViewData { +impl Deref for DeckViewData { type Target = DeckSettings; fn deref(&self) -> &Self::Target {