From 49c924415e98e78b2ea5a18e0d2777a571fb3ac9 Mon Sep 17 00:00:00 2001 From: Joris Date: Fri, 7 Feb 2025 11:23:57 +0100 Subject: Move db cards to specific function --- src/db/cards.rs | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/db/mod.rs | 160 +------------------------------------------------------- src/gui/mod.rs | 8 +-- src/sync.rs | 8 +-- 4 files changed, 170 insertions(+), 166 deletions(-) create mode 100644 src/db/cards.rs (limited to 'src') diff --git a/src/db/cards.rs b/src/db/cards.rs new file mode 100644 index 0000000..6d3e28d --- /dev/null +++ b/src/db/cards.rs @@ -0,0 +1,160 @@ +use anyhow::Result; +use rusqlite::{params, Connection}; + +use crate::model::DbEntry; +use crate::util::time; +use crate::{ + model::{Card, Question}, + space_repetition, + util::serialization, +}; + +pub fn all(conn: &Connection) -> Result> { + let mut stmt = conn.prepare("SELECT question, responses, deleted FROM cards")?; + + let res: Result, rusqlite::Error> = stmt + .query_map([], |row| { + let responses: String = row.get(1)?; + Ok(DbEntry { + question: row.get(0)?, + responses: serialization::line_to_words(&responses), + deleted: row.get(2)?, + }) + })? + .collect(); + + Ok(res?) +} + +pub fn insert(conn: &mut Connection, questions: &Vec) -> Result<()> { + let now = time::seconds_since_unix_epoch()?; + let state = serde_json::to_string(&space_repetition::init())?; + + let tx = conn.transaction()?; + for Question { + question, + responses, + } in questions + { + let responses = serialization::words_to_line(responses); + tx.execute( + " + INSERT INTO cards (question, responses, state, created, ready) + VALUES (?, ?, ?, ?, ?) + ", + params![question, responses, state, now, now], + )?; + } + tx.commit()?; + Ok(()) +} + +pub fn delete(conn: &mut Connection, questions: &Vec) -> Result<()> { + let now = time::seconds_since_unix_epoch()?; + + let tx = conn.transaction()?; + for Question { + question, + responses, + } in questions + { + let responses = serialization::words_to_line(responses); + tx.execute( + "UPDATE cards SET deleted = ? WHERE question = ? AND responses = ?", + params![now, question, responses], + )?; + } + tx.commit()?; + Ok(()) +} + +pub fn undelete(conn: &mut Connection, questions: &Vec) -> Result<()> { + let tx = conn.transaction()?; + for Question { + question, + responses, + } in questions + { + let responses = serialization::words_to_line(responses); + tx.execute( + "UPDATE cards SET deleted = NULL WHERE question = ? AND responses = ?", + params![question, responses], + )?; + } + tx.commit()?; + Ok(()) +} + +pub fn pick_random_ready(conn: &Connection) -> Option { + let now = time::seconds_since_unix_epoch().ok()?; + + let mut stmt = conn + .prepare( + " + SELECT question, responses, state, ready + FROM cards + WHERE deleted IS NULL AND ready <= ? + ORDER BY RANDOM() + LIMIT 1 + ", + ) + .ok()?; + + let mut rows = stmt.query([now]).ok()?; + let row = rows.next().ok()??; + let state_str: String = row.get(2).ok()?; + let responses_str: String = row.get(1).ok()?; + + Some(Card { + question: row.get(0).ok()?, + responses: serialization::line_to_words(&responses_str), + state: serde_json::from_str(&state_str).ok()?, + ready: row.get(3).ok()?, + }) +} + +pub fn next_ready(conn: &Connection) -> Option { + let mut stmt = conn + .prepare( + " + SELECT ready + FROM cards + WHERE deleted IS NULL + ORDER BY ready + LIMIT 1 + ", + ) + .ok()?; + + let mut rows = stmt.query([]).ok()?; + let row = rows.next().ok()??; + row.get(0).ok()? +} + +pub fn count_available(conn: &Connection) -> Option { + let now = time::seconds_since_unix_epoch().ok()?; + let mut stmt = conn + .prepare("SELECT COUNT(*) FROM cards WHERE ready <= ? AND deleted IS NULL") + .ok()?; + + let mut rows = stmt.query([now]).ok()?; + let row = rows.next().ok()??; + row.get(0).ok()? +} + +pub fn update(conn: &Connection, question: &str, state: &space_repetition::State) -> Result<()> { + let now = time::seconds_since_unix_epoch()?; + let ready = now + state.get_interval_seconds(); + let state_str = serde_json::to_string(state)?; + + conn.execute( + " + UPDATE cards + SET state = ?, updated = ?, ready = ? + WHERE question = ? + ", + params![state_str, now, ready, question], + )?; + + Ok(()) +} diff --git a/src/db/mod.rs b/src/db/mod.rs index 434d74a..54bf90f 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,14 +1,8 @@ use anyhow::Result; -use rusqlite::{params, Connection}; +use rusqlite::Connection; use rusqlite_migration::{Migrations, M}; -use crate::model::DbEntry; -use crate::util::time; -use crate::{ - model::{Card, Question}, - space_repetition, - util::serialization, -}; +pub mod cards; pub fn init(database: String) -> Result { let mut conn = Connection::open(database)?; @@ -20,153 +14,3 @@ pub fn init(database: String) -> Result { migrations.to_latest(&mut conn)?; Ok(conn) } - -pub fn all(conn: &Connection) -> Result> { - let mut stmt = conn.prepare("SELECT question, responses, deleted FROM cards")?; - - let res: Result, rusqlite::Error> = stmt - .query_map([], |row| { - let responses: String = row.get(1)?; - Ok(DbEntry { - question: row.get(0)?, - responses: serialization::line_to_words(&responses), - deleted: row.get(2)?, - }) - })? - .collect(); - - Ok(res?) -} - -pub fn insert(conn: &mut Connection, questions: &Vec) -> Result<()> { - let now = time::seconds_since_unix_epoch()?; - let state = serde_json::to_string(&space_repetition::init())?; - - let tx = conn.transaction()?; - for Question { - question, - responses, - } in questions - { - let responses = serialization::words_to_line(responses); - tx.execute( - " - INSERT INTO cards (question, responses, state, created, ready) - VALUES (?, ?, ?, ?, ?) - ", - params![question, responses, state, now, now], - )?; - } - tx.commit()?; - Ok(()) -} - -pub fn delete(conn: &mut Connection, questions: &Vec) -> Result<()> { - let now = time::seconds_since_unix_epoch()?; - - let tx = conn.transaction()?; - for Question { - question, - responses, - } in questions - { - let responses = serialization::words_to_line(responses); - tx.execute( - "UPDATE cards SET deleted = ? WHERE question = ? AND responses = ?", - params![now, question, responses], - )?; - } - tx.commit()?; - Ok(()) -} - -pub fn undelete(conn: &mut Connection, questions: &Vec) -> Result<()> { - let tx = conn.transaction()?; - for Question { - question, - responses, - } in questions - { - let responses = serialization::words_to_line(responses); - tx.execute( - "UPDATE cards SET deleted = NULL WHERE question = ? AND responses = ?", - params![question, responses], - )?; - } - tx.commit()?; - Ok(()) -} - -pub fn pick_random_ready(conn: &Connection) -> Option { - let now = time::seconds_since_unix_epoch().ok()?; - - let mut stmt = conn - .prepare( - " - SELECT question, responses, state, ready - FROM cards - WHERE deleted IS NULL AND ready <= ? - ORDER BY RANDOM() - LIMIT 1 - ", - ) - .ok()?; - - let mut rows = stmt.query([now]).ok()?; - let row = rows.next().ok()??; - let state_str: String = row.get(2).ok()?; - let responses_str: String = row.get(1).ok()?; - - Some(Card { - question: row.get(0).ok()?, - responses: serialization::line_to_words(&responses_str), - state: serde_json::from_str(&state_str).ok()?, - ready: row.get(3).ok()?, - }) -} - -pub fn next_ready(conn: &Connection) -> Option { - let mut stmt = conn - .prepare( - " - SELECT ready - FROM cards - WHERE deleted IS NULL - ORDER BY ready - LIMIT 1 - ", - ) - .ok()?; - - let mut rows = stmt.query([]).ok()?; - let row = rows.next().ok()??; - row.get(0).ok()? -} - -pub fn count_available(conn: &Connection) -> Option { - let now = time::seconds_since_unix_epoch().ok()?; - let mut stmt = conn - .prepare("SELECT COUNT(*) FROM cards WHERE ready <= ? AND deleted IS NULL") - .ok()?; - - let mut rows = stmt.query([now]).ok()?; - let row = rows.next().ok()??; - row.get(0).ok()? -} - -pub fn update(conn: &Connection, question: &str, state: &space_repetition::State) -> Result<()> { - let now = time::seconds_since_unix_epoch()?; - let ready = now + state.get_interval_seconds(); - let state_str = serde_json::to_string(state)?; - - conn.execute( - " - UPDATE cards - SET state = ?, updated = ?, ready = ? - WHERE question = ? - ", - params![state_str, now, ready, question], - )?; - - Ok(()) -} diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 3abe238..d721b90 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -47,15 +47,15 @@ pub fn start( let title = title( deck_name, - db::count_available(conn).unwrap_or(0), + db::cards::count_available(conn).unwrap_or(0), hide_remaining, ); - match db::pick_random_ready(conn) { + match db::cards::pick_random_ready(conn) { Some(card) => match question::ask(term, &title, &card)? { question::Response::Aborted => break, question::Response::Answered { difficulty } => { - db::update( + db::cards::update( conn, &card.question, &space_repetition::update(card.state, difficulty), @@ -63,7 +63,7 @@ pub fn start( } }, None => { - let message = match db::next_ready(conn) { + let message = match db::cards::next_ready(conn) { Some(ready) => { let now = time::seconds_since_unix_epoch()?; let duration = time::pp_duration(ready - now); diff --git a/src/sync.rs b/src/sync.rs index 974e838..f96d93a 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use std::collections::HashSet; pub fn run(conn: &mut Connection, deck_path: &str) -> Result<()> { - let db_entries = db::all(conn)?; + let db_entries = db::cards::all(conn)?; let lines = deck::read_file(deck_path)?; let Diff { new, @@ -16,9 +16,9 @@ pub fn run(conn: &mut Connection, deck_path: &str) -> Result<()> { undeleted, } = diff(db_entries, lines); - db::insert(conn, &new)?; - db::delete(conn, &deleted)?; - db::undelete(conn, &undeleted)?; + db::cards::insert(conn, &new)?; + db::cards::delete(conn, &deleted)?; + db::cards::undelete(conn, &undeleted)?; Ok(()) } -- cgit v1.2.3