use crate::app::AppState; use crate::core::srs::{FADE_PER_DAY, MAX_SCORE, OrderMode, WordOpenMode}; use crate::data::{INVALID_ID, Id}; use crate::notification::MaybeError; use chrono::{DateTime, Utc}; use log::info; use rayon::prelude::*; use rhai::{Engine, Scope}; use std::collections::HashMap; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::time::Instant; #[derive(Clone, Debug)] pub struct WordData { pub id: Id, pub key: String, pub value: String, pub tags: String, pub additional: HashMap, pub group_id: Id, } #[derive(Clone)] pub struct WordGroup { pub id: Id, pub name: String, } impl WordData { pub fn new() -> Self { Self { id: INVALID_ID, key: String::new(), value: String::new(), tags: String::new(), additional: Default::default(), group_id: 1.into(), } } } #[derive(Clone)] pub struct HistoryItem { pub timestamp: DateTime, pub word_id: Id, pub mode: WordOpenMode, pub before: u8, pub after: u8, } #[derive(Clone)] pub struct DeckSettings { pub id: Id, pub name: String, pub forward: String, pub backward: String, pub filter: String, pub count: Option, pub open_mode: OrderMode, } impl DeckSettings { pub(crate) fn with_name(name: String) -> DeckSettings { DeckSettings { id: 0.into(), name, forward: "".to_string(), backward: "".to_string(), filter: "true".to_string(), count: None, open_mode: OrderMode::Default, } } pub fn check_filter(&self) -> bool { let time = Instant::now(); let engine = Engine::new(); let ast = engine.compile(&self.filter); info!("Compile time is {:?}", time.elapsed()); ast.is_ok() } pub fn get_word_list(&self, state: &AppState) -> Vec { let time = Instant::now(); let mut list = vec![]; let engine = Engine::new(); let ast = engine.compile(&self.filter); if let Ok(ast) = ast { let groups = &state.word_groups; list = state .dictionary .par_iter() .enumerate() .filter(|(_, word)| { let mut more = rhai::Map::new(); for iced in &word.additional { more.insert(iced.0.clone().into(), iced.1.clone().into()); } let mut scope = Scope::new(); scope .push_constant("id", word.id.0 as i64) .push_constant("key", word.key.clone()) .push_constant("value", word.value.clone()) .push_constant("tags", word.tags.clone()) .push_constant("more", more) .push_constant( "group", groups .iter() .find(|g| g.id == word.group_id) .cloned() .expect("No group id") .name, ); let result = engine.eval_ast_with_scope::(&mut scope, &ast); if let Ok(result) = result { return result; } false }) .map(|(index, _)| index) .collect(); info!("Collecting available words is {:?}", time.elapsed()); list } else { list } } pub fn require_speech(&self) -> bool { self.forward == "speech" || self.backward == "speech" } } #[derive(Clone, PartialEq)] pub struct CardStatistics { pub id: Id, pub word_id: Id, pub set_id: Id, pub last_open: DateTime, pub score: u8, } impl CardStatistics { pub fn update(&mut self, status: WordOpenMode) { match status { WordOpenMode::Easy => { self.score = (self.calculated_score() + 5.0).round() as u8; } WordOpenMode::Ok => self.score = (self.calculated_score() + 2.0).round() as u8, WordOpenMode::Hard => { self.score = (self.calculated_score() * 0.75).round() as u8; } WordOpenMode::None => { self.score = (self.calculated_score() * 0.4) as u8; } } self.score = self.score.clamp(1, MAX_SCORE); self.last_open = Utc::now(); } pub fn calculated_score(&self) -> f32 { let time = Utc::now() - self.last_open; let days = time.num_days(); let multiplier = FADE_PER_DAY.powi(days as i32); (self.score as f32 * multiplier).max(1.0) } } #[derive(Clone)] pub struct Entity> { pub(crate) repo: R, pub(crate) _phantom: PhantomData, pub(crate) inner: EntityInner, } #[derive(Clone)] pub(crate) struct EntityInner { pub(crate) id: Id, pub(crate) real: bool, } impl> Entity { pub fn load(&self) -> EntityAccess { let clone = *self.clone(); let repo = &self.repo; let value = repo.get(self.inner.id).unwrap(); EntityAccess { value, entity: clone, } } } pub struct EntityAccess> { entity: Entity, value: E, } impl> Deref for EntityAccess { type Target = E; fn deref(&self) -> &Self::Target { &self.value } } impl> DerefMut for EntityAccess { fn deref_mut(&mut self) -> &mut E { &mut self.value } } pub trait Repository { fn get(&self, id: Id) -> Option; fn create(&mut self, new: T) -> Entity>; fn save(&mut self, entity: EntityInner) -> MaybeError; }