Files
jap_learn/src/data_provider/mod.rs
T

171 lines
4.8 KiB
Rust
Raw Normal View History

2026-08-30 20:32:16 +03:00
use crate::data::Id;
2026-08-30 20:43:31 +03:00
use crate::data::entities::{CardState, EntityData};
2026-08-30 20:32:16 +03:00
use crate::data::repositories::{BatchedDbLayer, DbLayer};
use crate::notification::MaybeError;
use crate::notification::NotificationData::{DbLoadError, DbSaveError};
use log::info;
2026-08-30 20:43:31 +03:00
use rusqlite::{Connection, Error, Params, Row};
2026-08-30 20:32:16 +03:00
use std::marker::PhantomData;
use std::time::Instant;
2026-08-29 21:32:54 +03:00
pub(crate) mod card_states;
2026-08-29 22:02:01 +03:00
pub(crate) mod decks;
2026-08-01 19:43:07 +03:00
pub(crate) mod history;
2026-08-15 21:32:24 +03:00
pub(crate) mod import;
2026-06-01 15:36:53 +03:00
pub(crate) mod settings;
2026-06-15 17:06:02 +03:00
pub(crate) mod sqlite;
2026-08-01 19:43:07 +03:00
pub(crate) mod voice;
2026-08-13 09:06:22 +03:00
pub(crate) mod web_api;
2026-08-15 21:32:24 +03:00
pub(crate) mod words;
2026-08-30 20:32:16 +03:00
pub trait TableAccessQuerySet<T> {
fn load() -> &'static str;
fn save() -> &'static str;
fn add() -> &'static str;
fn name() -> &'static str;
2026-08-30 20:43:31 +03:00
fn from_sql(row: &Row) -> Result<T, Error>;
2026-08-30 20:32:16 +03:00
fn insert_params(entity: &T) -> impl Params;
fn update_params(entity: &T) -> impl Params;
}
pub struct SqliteDbLayer<T, Q>
where
Q: TableAccessQuerySet<T>,
{
2026-08-30 20:43:31 +03:00
_phantom: PhantomData<(T, Q)>,
2026-08-30 20:32:16 +03:00
}
impl<T: EntityData, Q: TableAccessQuerySet<T>> DbLayer<T> for SqliteDbLayer<T, Q> {
fn write_entity(&self, entity: &T, connection: &Connection) -> MaybeError {
2026-08-30 22:36:43 +03:00
let time = Instant::now();
2026-08-30 20:32:16 +03:00
connection
.execute(Q::load(), Q::update_params(entity))
.map_err(|_| DbSaveError)?;
2026-08-30 22:36:43 +03:00
info!(
"Updated in {} for {:?}",
Q::name(),
time.elapsed()
);
2026-08-30 20:32:16 +03:00
Ok(())
}
fn add_entity(&mut self, entity: &mut T, connection: &Connection) -> MaybeError {
2026-08-30 22:36:43 +03:00
let time = Instant::now();
2026-08-30 20:32:16 +03:00
let index: u32 = connection
.query_row(Q::add(), Q::insert_params(entity), |row| row.get(0))
.map_err(|_| DbSaveError)?;
entity.set_id(index.into());
2026-08-30 22:36:43 +03:00
info!(
"Added into {} for {:?}",
Q::name(),
time.elapsed()
);
2026-08-30 20:32:16 +03:00
Ok(())
}
fn delete_entity(&mut self, id: Id, connection: &Connection) -> MaybeError {
2026-08-30 22:36:43 +03:00
let time = Instant::now();
2026-08-30 20:32:16 +03:00
if !id.is_valid() {
return Err(DbSaveError);
}
connection
.execute(
format!("DELETE FROM {} WHERE id = ?1", Q::name()).as_str(),
(&id,),
)
.map_err(|_| DbSaveError)?;
2026-08-30 22:36:43 +03:00
info!(
"Deleted from {} for {:?}",
Q::name(),
time.elapsed()
);
2026-08-30 20:32:16 +03:00
Ok(())
}
fn load_all(&self, storage: &mut Vec<T>, connection: &Connection) -> MaybeError {
2026-08-30 22:36:43 +03:00
let time = Instant::now();
2026-08-30 20:32:16 +03:00
let mut stmt = connection
.prepare(Q::load())
.expect("Failed to prepare state query");
let iter = stmt
2026-08-30 20:46:52 +03:00
.query_map([], |row| Ok(Q::from_sql(row)?))
2026-08-30 20:32:16 +03:00
.map_err(|_| DbLoadError)?;
iter.filter_map(|res| res.ok())
.for_each(|i| storage.push(i));
2026-08-30 22:36:43 +03:00
info!(
"Loaded {} from {} for {:?}",
storage.len(),
Q::name(),
time.elapsed()
);
2026-08-30 20:32:16 +03:00
Ok(())
}
}
impl<T: EntityData, Q: TableAccessQuerySet<T>> BatchedDbLayer<T> for SqliteDbLayer<T, Q> {
fn delete_many_entities(&mut self, ids: &[Id], connection: &mut Connection) -> MaybeError {
2026-08-30 22:36:43 +03:00
let time = Instant::now();
2026-08-30 20:32:16 +03:00
for chunk in ids.chunks(1000) {
let stringed = chunk
.iter()
.map(|i| i.to_string())
.collect::<Vec<_>>()
.join(",");
connection
.execute(
format!("DELETE FROM {} WHERE id in ({})", Q::name(), stringed).as_str(),
(),
)
.map_err(|_| DbSaveError)?;
}
2026-08-30 22:36:43 +03:00
info!(
"Deleted {} from {} for {:?}",
ids.len(),
Q::name(),
time.elapsed()
);
2026-08-30 20:32:16 +03:00
Ok(())
}
fn add_many_entities(&mut self, entities: &mut [T], connection: &mut Connection) -> MaybeError {
let time = Instant::now();
let tx = connection
.transaction()
.expect("Failed to create transaction");
let count = entities.len();
{
let mut stmt = tx.prepare(Q::add()).expect("Failed to prepare state query");
for entity in entities.iter() {
stmt.execute(Q::insert_params(entity))
.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() {
entities[index].set_id(id.into());
}
2026-08-30 22:36:43 +03:00
info!(
"Added {} into {} for {:?}",
entities.len(),
Q::name(),
time.elapsed()
);
2026-08-30 20:32:16 +03:00
Ok(())
}
}