5 Commits
11 changed files with 125 additions and 67 deletions

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
create table custom_fields(
id INTEGER PRIMARY KEY,
name TEXT not null
);
+14 -2
View File
@@ -3,7 +3,7 @@ pub mod repositories;
use rusqlite::ToSql; use rusqlite::ToSql;
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef}; use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef};
use std::fmt::{write, Display, Formatter}; use std::fmt::{Display, Formatter, write};
use std::num::ParseIntError; use std::num::ParseIntError;
use std::str::FromStr; use std::str::FromStr;
@@ -63,8 +63,20 @@ macro_rules! id_impl {
self.0 != 0 self.0 != 0
} }
} }
} };
} }
id_impl!(u32, Id, INVALID_ID); id_impl!(u32, Id, INVALID_ID);
id_impl!(u16, SmallId, INVALID_SMALL_ID); id_impl!(u16, SmallId, INVALID_SMALL_ID);
impl From<SmallId> for Id {
fn from(value: SmallId) -> Self {
Self(value.0 as u32)
}
}
impl From<Id> for SmallId {
fn from(value: Id) -> Self {
Self(value.0 as u16)
}
}
+1 -1
View File
@@ -3,13 +3,13 @@ use crate::core::srs::{FADE_PER_DAY, MAX_SCORE, OrderMode, WordOpenMode};
use crate::data::{INVALID_ID, Id}; use crate::data::{INVALID_ID, Id};
use chrono::{DateTime, NaiveDate, Utc}; use chrono::{DateTime, NaiveDate, Utc};
use fsrs::MemoryState; use fsrs::MemoryState;
use litemap::LiteMap;
use log::info; use log::info;
use rayon::prelude::*; use rayon::prelude::*;
use rhai::{Engine, Scope}; use rhai::{Engine, Scope};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::time::Instant; use std::time::Instant;
use litemap::LiteMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct WordData { pub struct WordData {
+3 -2
View File
@@ -320,7 +320,8 @@ impl<T: EntityData + Clone, D: DbLayer<T>> Repository<T, D> for DataRepositoryIn
} }
fn create_now(&mut self, mut new: T) -> Result<EntityHandle<T>, NotificationData> { fn create_now(&mut self, mut new: T) -> Result<EntityHandle<T>, NotificationData> {
self.data_layer.add_entity(&mut new, &self.connection.read())?; self.data_layer
.add_entity(&mut new, &self.connection.read())?;
let index = self.cache.len(); let index = self.cache.len();
let handle = EntityHandle::new(new.id(), index, true); let handle = EntityHandle::new(new.id(), index, true);
self.cache.push(new); self.cache.push(new);
@@ -378,7 +379,7 @@ impl<T: EntityData + Clone, D: DbLayer<T>> Repository<T, D> for DataRepositoryIn
fn start(&mut self) -> MaybeError { fn start(&mut self) -> MaybeError {
if self.version != 0 { if self.version != 0 {
return Ok(()) return Ok(());
} }
self.init()?; self.init()?;
+37
View File
@@ -0,0 +1,37 @@
use crate::data::entities::WordGroup;
use crate::data_provider::TableAccessQuerySet;
use rusqlite::{Error, Params, Row};
pub struct CustomFieldsQuerySet;
impl TableAccessQuerySet<WordGroup> for CustomFieldsQuerySet {
fn load() -> &'static str {
"SELECT id, name FROM custom_fields"
}
fn save() -> &'static str {
"UPDATE custom_fields SET name = ?1 WHERE id = ?2"
}
fn add() -> &'static str {
"INSERT INTO word_groups (name) VALUES (?1)"
}
fn name() -> &'static str {
"custom_fields"
}
fn from_sql(row: &Row) -> Result<WordGroup, Error> {
Ok(WordGroup {
id: row.get(0)?,
name: row.get(1)?,
})
}
fn insert_params(entity: &WordGroup) -> impl Params {
(&entity.name,)
}
fn update_params(entity: &WordGroup) -> impl Params {
(&entity.name, entity.id)
}
}
+7 -1
View File
@@ -46,7 +46,13 @@ impl TableAccessQuerySet<DeckSettings> for DeckQuerySet {
} }
fn update_params(entity: &DeckSettings) -> impl Params { fn update_params(entity: &DeckSettings) -> impl Params {
(&entity.name, &entity.forward, &entity.backward, &entity.filter, entity.id) (
&entity.name,
&entity.forward,
&entity.backward,
&entity.filter,
entity.id,
)
} }
} }
+9 -19
View File
@@ -3,12 +3,13 @@ use crate::data::entities::{CardState, EntityData};
use crate::data::repositories::{BatchedDbLayer, DbLayer}; use crate::data::repositories::{BatchedDbLayer, DbLayer};
use crate::notification::MaybeError; use crate::notification::MaybeError;
use crate::notification::NotificationData::{DbLoadError, DbSaveError}; use crate::notification::NotificationData::{DbLoadError, DbSaveError};
use log::info; use log::debug;
use rusqlite::{Connection, Error, Params, Row}; use rusqlite::{Connection, Error, Params, Row};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::time::Instant; use std::time::Instant;
pub(crate) mod card_states; pub(crate) mod card_states;
pub mod custom_fields;
pub(crate) mod decks; pub(crate) mod decks;
pub(crate) mod history; pub(crate) mod history;
pub(crate) mod import; pub(crate) mod import;
@@ -16,6 +17,7 @@ pub(crate) mod settings;
pub(crate) mod sqlite; pub(crate) mod sqlite;
pub(crate) mod voice; pub(crate) mod voice;
pub(crate) mod web_api; pub(crate) mod web_api;
pub mod word_groups;
pub(crate) mod words; pub(crate) mod words;
pub trait TableAccessQuerySet<T> { pub trait TableAccessQuerySet<T> {
@@ -42,11 +44,7 @@ impl<T: EntityData, Q: TableAccessQuerySet<T>> DbLayer<T> for SqliteDbLayer<T, Q
connection connection
.execute(Q::load(), Q::update_params(entity)) .execute(Q::load(), Q::update_params(entity))
.map_err(|_| DbSaveError)?; .map_err(|_| DbSaveError)?;
info!( debug!("Updated in {} for {:?}", Q::name(), time.elapsed());
"Updated in {} for {:?}",
Q::name(),
time.elapsed()
);
Ok(()) Ok(())
} }
@@ -58,11 +56,7 @@ impl<T: EntityData, Q: TableAccessQuerySet<T>> DbLayer<T> for SqliteDbLayer<T, Q
.map_err(|_| DbSaveError)?; .map_err(|_| DbSaveError)?;
entity.set_id(index.into()); entity.set_id(index.into());
info!( debug!("Added into {} for {:?}", Q::name(), time.elapsed());
"Added into {} for {:?}",
Q::name(),
time.elapsed()
);
Ok(()) Ok(())
} }
@@ -78,11 +72,7 @@ impl<T: EntityData, Q: TableAccessQuerySet<T>> DbLayer<T> for SqliteDbLayer<T, Q
(&id,), (&id,),
) )
.map_err(|_| DbSaveError)?; .map_err(|_| DbSaveError)?;
info!( debug!("Deleted from {} for {:?}", Q::name(), time.elapsed());
"Deleted from {} for {:?}",
Q::name(),
time.elapsed()
);
Ok(()) Ok(())
} }
@@ -98,7 +88,7 @@ impl<T: EntityData, Q: TableAccessQuerySet<T>> DbLayer<T> for SqliteDbLayer<T, Q
iter.filter_map(|res| res.ok()) iter.filter_map(|res| res.ok())
.for_each(|i| storage.push(i)); .for_each(|i| storage.push(i));
info!( debug!(
"Loaded {} from {} for {:?}", "Loaded {} from {} for {:?}",
storage.len(), storage.len(),
Q::name(), Q::name(),
@@ -126,7 +116,7 @@ impl<T: EntityData, Q: TableAccessQuerySet<T>> BatchedDbLayer<T> for SqliteDbLay
) )
.map_err(|_| DbSaveError)?; .map_err(|_| DbSaveError)?;
} }
info!( debug!(
"Deleted {} from {} for {:?}", "Deleted {} from {} for {:?}",
ids.len(), ids.len(),
Q::name(), Q::name(),
@@ -160,7 +150,7 @@ impl<T: EntityData, Q: TableAccessQuerySet<T>> BatchedDbLayer<T> for SqliteDbLay
entities[index].set_id(id.into()); entities[index].set_id(id.into());
} }
info!( debug!(
"Added {} into {} for {:?}", "Added {} into {} for {:?}",
entities.len(), entities.len(),
Q::name(), Q::name(),
+7
View File
@@ -21,6 +21,7 @@ fn create_tables(conn: &Connection) {
make_words(conn).expect("Failed to init table"); make_words(conn).expect("Failed to init table");
make_card_stats(conn).expect("Failed to init table"); make_card_stats(conn).expect("Failed to init table");
make_history(conn).expect("Failed to init table"); make_history(conn).expect("Failed to init table");
make_custom_fields(conn).expect("Failed to init table");
} }
fn make_settings(conn: &Connection) -> Result<(), rusqlite::Error> { fn make_settings(conn: &Connection) -> Result<(), rusqlite::Error> {
@@ -58,6 +59,12 @@ fn make_history(conn: &Connection) -> Result<(), rusqlite::Error> {
Ok(()) Ok(())
} }
fn make_custom_fields(conn: &Connection) -> Result<(), rusqlite::Error> {
let query = include_str!("../../sql/custom_fields.sql");
conn.execute(query, ())?;
Ok(())
}
pub fn default_connection() -> Connection { pub fn default_connection() -> Connection {
let db_file = db_path(); let db_file = db_path();
let connection = Connection::open(db_file).expect("Failed to open database"); let connection = Connection::open(db_file).expect("Failed to open database");
+37
View File
@@ -0,0 +1,37 @@
use crate::data::entities::WordGroup;
use crate::data_provider::TableAccessQuerySet;
use rusqlite::{Error, Params, Row};
pub struct WordGroupQuerySet;
impl TableAccessQuerySet<WordGroup> for WordGroupQuerySet {
fn load() -> &'static str {
"SELECT id, name FROM word_groups"
}
fn save() -> &'static str {
"UPDATE word_groups SET name = ?1 WHERE id = ?2"
}
fn add() -> &'static str {
"INSERT INTO word_groups (name) VALUES (?1)"
}
fn name() -> &'static str {
"word_groups"
}
fn from_sql(row: &Row) -> Result<WordGroup, Error> {
Ok(WordGroup {
id: row.get(0)?,
name: row.get(1)?,
})
}
fn insert_params(entity: &WordGroup) -> impl Params {
(&entity.name,)
}
fn update_params(entity: &WordGroup) -> impl Params {
(&entity.name, entity.id)
}
}
+3 -39
View File
@@ -2,9 +2,9 @@ use crate::data::entities::{WordData, WordGroup};
use crate::data_provider::TableAccessQuerySet; use crate::data_provider::TableAccessQuerySet;
use crate::notification::NotificationData::{DbLoadError, DbSaveError}; use crate::notification::NotificationData::{DbLoadError, DbSaveError};
use crate::notification::{MaybeError, NotificationData}; use crate::notification::{MaybeError, NotificationData};
use litemap::LiteMap;
use rusqlite::{Connection, Error, Params, Row, params}; use rusqlite::{Connection, Error, Params, Row, params};
use std::collections::HashMap; use std::collections::HashMap;
use litemap::LiteMap;
pub struct WordQuerySet; pub struct WordQuerySet;
impl TableAccessQuerySet<WordData> for WordQuerySet { impl TableAccessQuerySet<WordData> for WordQuerySet {
@@ -31,8 +31,7 @@ impl TableAccessQuerySet<WordData> for WordQuerySet {
key: row.get(1)?, key: row.get(1)?,
value: row.get(2)?, value: row.get(2)?,
tags: row.get(3)?, tags: row.get(3)?,
additional: serde_json::from_str(&additional) additional: serde_json::from_str(&additional).unwrap_or_default(),
.unwrap_or_default(),
group_id: row.get(5)?, group_id: row.get(5)?,
}) })
} }
@@ -58,40 +57,6 @@ impl TableAccessQuerySet<WordData> for WordQuerySet {
} }
} }
pub struct WordGroupQuerySet;
impl TableAccessQuerySet<WordGroup> for WordGroupQuerySet {
fn load() -> &'static str {
"SELECT id, name FROM word_groups"
}
fn save() -> &'static str {
"UPDATE word_groups SET name = ?1 WHERE id = ?2"
}
fn add() -> &'static str {
"INSERT INTO word_groups (name) VALUES (?1)"
}
fn name() -> &'static str {
"word_groups"
}
fn from_sql(row: &Row) -> Result<WordGroup, Error> {
Ok(WordGroup {
id: row.get(0)?,
name: row.get(1)?,
})
}
fn insert_params(entity: &WordGroup) -> impl Params {
(&entity.name,)
}
fn update_params(entity: &WordGroup) -> impl Params {
(&entity.name, entity.id)
}
}
pub fn add_word(word: &mut WordData, connection: &Connection) -> MaybeError { pub fn add_word(word: &mut WordData, connection: &Connection) -> MaybeError {
let index : u32 = connection let index : u32 = connection
.query_row( .query_row(
@@ -187,8 +152,7 @@ pub fn load_words(connection: &Connection) -> Result<Vec<WordData>, Notification
key: row.get(1)?, key: row.get(1)?,
value: row.get(2)?, value: row.get(2)?,
tags: row.get(3)?, tags: row.get(3)?,
additional: serde_json::from_str(&additional) additional: serde_json::from_str(&additional).unwrap_or_default(),
.unwrap_or_default(),
group_id: row.get(5)?, group_id: row.get(5)?,
}) })
}) })
+3 -3
View File
@@ -37,7 +37,7 @@ use iced::keyboard::Event;
use iced::widget::stack; use iced::widget::stack;
use iced::{Element, Task}; use iced::{Element, Task};
use iced_core::Length::Fill; use iced_core::Length::Fill;
use log::info; use log::{debug, info};
use parking_lot::Mutex; use parking_lot::Mutex;
use rusqlite::Connection; use rusqlite::Connection;
use std::sync::Arc; use std::sync::Arc;
@@ -241,7 +241,7 @@ impl ScreenState {
Import Import
); );
} }
info!("Update took {:?}", time.elapsed()); debug!("Update took {:?}", time.elapsed());
task.0 task.0
} }
@@ -288,7 +288,7 @@ impl ScreenState {
let notifications = self.notification_manager.draw().map(Notification); let notifications = self.notification_manager.draw().map(Notification);
info!("View took {:?}", time.elapsed()); debug!("View took {:?}", time.elapsed());
stack![view, notifications].width(Fill).height(Fill).into() stack![view, notifications].width(Fill).height(Fill).into()
} }