use crate::data::entities::{CardStatistics, DeckSettings}; use crate::notification::NotificationData::{DbLoadError, DbSaveError}; use crate::notification::{MaybeError, NotificationData}; use log::info; use rusqlite::{Connection, params}; use std::time::Instant; pub fn load_stats_of_deck( set: &DeckSettings, connection: &Connection, ) -> Result, NotificationData> { let mut stmt = connection .prepare("SELECT id, word_id, score, last_opened FROM card_stats WHERE set_id = ?1") .expect("Failed to prepare state query"); let iter = stmt .query_map((set.id,), |row| { Ok(CardStatistics { id: row.get(0)?, word_id: row.get(1)?, set_id: set.id, score: row.get(2)?, last_open: row.get(3)?, }) }) .map_err(|_| DbLoadError)?; let mut buffer = Vec::with_capacity(512); iter.filter_map(|res| res.ok()).for_each(|i| buffer.push(i)); Ok(buffer) } pub fn add_stat_list(stats: &mut [CardStatistics], connection: &mut Connection) -> MaybeError { let time = Instant::now(); let tx = connection .transaction() .expect("Failed to create transaction"); let count = stats.len(); { let mut stmt = tx .prepare( "INSERT INTO card_stats (word_id, set_id, score, last_opened) VALUES (?1, ?2, ?3, ?4)", ) .expect("Failed to prepare state query"); for stat in stats.iter() { stmt.execute(params![ stat.word_id, stat.set_id, stat.score, stat.last_open.timestamp() ]) .map_err(|_| DbSaveError)?; } } let last_index: u32 = tx.last_insert_rowid() as u32; tx.commit().expect("Failed to commit"); let start_index = last_index - (count as u32) + 1; for (index, id) in (start_index..=last_index).enumerate() { stats[index].id = id.into(); } info!("Added {} cards for {:?}", stats.len(), time.elapsed()); Ok(()) } pub fn update_stat_score(stat: &CardStatistics, connection: &Connection) -> MaybeError { let time = Instant::now(); connection .execute( "UPDATE card_stats SET score = ?1, last_opened = ?2 WHERE id = ?3", (&stat.score, &stat.last_open.timestamp(), &stat.id), ) .map_err(|_| DbSaveError)?; info!("Updated stat: {}", time.elapsed().as_millis()); Ok(()) } pub fn delete_stats(stat: &[CardStatistics], connection: &Connection) -> MaybeError { if stat.is_empty() { return Ok(()); } let ids = stat .iter() .map(|s| s.id.into()) .collect::>() .join(", "); connection .execute( format!("DELETE FROM card_stats WHERE id in ({ids})").as_str(), (), ) .map_err(|_| DbSaveError)?; Ok(()) }