Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29a90d48d9 | ||
|
|
2fc58fcc2d | ||
|
|
6d363c8148 | ||
|
|
ba834ac9b1 | ||
|
|
97e301b0d6 |
No files matched your search
@@ -0,0 +1,4 @@
|
|||||||
|
create table custom_fields(
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT not null
|
||||||
|
);
|
||||||
+14
-2
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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()?;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
@@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user