use crate::data::entities::CardState; use crate::data::repositories::{DataRepository, DataRepositoryInner, Query}; use crate::data::Id; use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet}; use chrono::NaiveDate; use fsrs::MemoryState; use rusqlite::{Error, Params, Row}; use std::sync::Arc; pub type CardStateRepository = DataRepository; pub type CardStateRepositoryInner = DataRepositoryInner; pub type CardStateDbLayer = SqliteDbLayer; #[derive(Clone)] pub struct CardStatesQuerySet; impl TableAccessQuerySet for CardStatesQuerySet { fn load() -> &'static str { "SELECT id, word_id, deck_id, score, last_opened, fsrs_opened, fsrs_memory, fsrs_open_day FROM states" } fn save() -> &'static str { "UPDATE states SET score = ?1, last_opened = ?2, fsrs_opened = ?3, fsrs_memory = ?4, fsrs_open_day = ?5 WHERE id = ?6" } fn add() -> &'static str { "INSERT INTO states (word_id, deck_id, score, last_opened, fsrs_opened, fsrs_memory, fsrs_open_day) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)" } fn name() -> &'static str { "states" } fn from_sql(row: &Row) -> Result { Ok(CardState { id: row.get(0)?, word_id: row.get(1)?, deck_id: row.get(2)?, score: row.get(3)?, fsrs_opened: NaiveDate::from_epoch_days(row.get::(5)?) .expect("Incorrect date encoding"), fsrs_memory: get_memory(row.get(6)?), last_open: row.get(4)?, fsrs_open_day: NaiveDate::from_epoch_days(row.get::(7)?) .expect("Incorrect date encoding"), }) } fn insert_params(entity: &CardState) -> impl Params { ( entity.word_id, entity.deck_id, entity.score, entity.last_open.timestamp(), entity.fsrs_opened.to_epoch_days(), memory_to_i64(&entity.fsrs_memory), entity.fsrs_open_day.to_epoch_days(), ) } fn update_params(entity: &CardState) -> impl Params { ( entity.score, entity.last_open.timestamp(), entity.fsrs_opened.to_epoch_days(), memory_to_i64(&entity.fsrs_memory), entity.fsrs_open_day.to_epoch_days(), entity.id, ) } } fn get_memory(value: i64) -> Option { if value == 0 { return None; } Some(MemoryState { stability: f32::from_bits(value as u32), difficulty: f32::from_bits((value >> 32) as u32), }) } fn memory_to_i64(value: &Option) -> i64 { let Some(value) = &value else { return 0; }; value.stability.to_bits() as i64 | ((value.difficulty.to_bits() as i64) << 32) } impl CardStateRepositoryInner { pub fn deck_states( &self, deck_id: Id, ) -> Query { self.query(Arc::new(move |state| state.deck_id == deck_id)) } }