Rewrite words and groups into result

This commit is contained in:
micialware committed 2026-08-24 18:46:54 +03:00
1 parent c56d135ce8
commit 5a6747030b
7 files changed
+101 -72

No files matched your search

+2 -2
View File
@@ -94,9 +94,9 @@ pub fn fill_state(state: &mut AppState) {
let groups = load_word_groups(&state.connection); let groups = load_word_groups(&state.connection);
let setting = load_settings(&state.connection); let setting = load_settings(&state.connection);
state.dictionary = list; state.dictionary = list.unwrap_or_default();
state.decks = sets.unwrap_or_default(); state.decks = sets.unwrap_or_default();
state.word_groups = groups; state.word_groups = groups.unwrap_or_default();
state.sync_data = setting state.sync_data = setting
} }
+1 -4
View File
@@ -24,10 +24,7 @@ pub fn load_sets(connection: &Connection) -> Result<Vec<DeckSettings>, Notificat
.map_err(|_| DbLoadError)?; .map_err(|_| DbLoadError)?;
let mut buffer = Vec::with_capacity(16); let mut buffer = Vec::with_capacity(16);
for word in iter.filter_map(|result| result.ok()) { iter.filter_map(|res| res.ok()).for_each(|i| buffer.push(i));
buffer.push(word);
}
Ok(buffer) Ok(buffer)
} }
+1 -3
View File
@@ -25,9 +25,7 @@ pub fn load_stats_of_deck(
.map_err(|_| DbLoadError)?; .map_err(|_| DbLoadError)?;
let mut buffer = Vec::with_capacity(512); let mut buffer = Vec::with_capacity(512);
for word in iter.filter_map(|result| result.ok()) { iter.filter_map(|res| res.ok()).for_each(|i| buffer.push(i));
buffer.push(word);
}
Ok(buffer) Ok(buffer)
} }
+55 -43
View File
@@ -1,8 +1,10 @@
use crate::data::entities::{WordData, WordGroup};
use crate::notification::NotificationData;
use crate::notification::NotificationData::{DbLoadError, DbSaveError};
use rusqlite::{Connection, params}; use rusqlite::{Connection, params};
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::entities::{WordData, WordGroup};
pub fn add_word(word: &mut WordData, connection: &Connection) { pub fn add_word(word: &mut WordData, connection: &Connection) -> Result<(), NotificationData> {
let index : u32 = connection let index : u32 = connection
.query_row( .query_row(
"INSERT INTO words (key, value, tags, more, group_id) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING id", "INSERT INTO words (key, value, tags, more, group_id) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING id",
@@ -10,17 +12,21 @@ pub fn add_word(word: &mut WordData, connection: &Connection) {
&word.key, &word.key,
&word.value, &word.value,
&word.tags, &word.tags,
serde_json::to_string(&word.additional).unwrap(), serde_json::to_string(&word.additional).unwrap_or_default(),
&word.group_id, &word.group_id,
), ),
|row| row.get(0), |row| row.get(0),
).unwrap(); ).map_err(|_| DbSaveError)?;
word.id = index.into(); word.id = index.into();
Ok(())
} }
pub fn add_words(words: &mut [WordData], connection: &mut Connection) { pub fn add_words(
let tx = connection.transaction().unwrap(); words: &mut [WordData],
connection: &mut Connection,
) -> Result<(), NotificationData> {
let tx = connection.transaction().map_err(|_| DbLoadError)?;
let count = words.len(); let count = words.len();
{ {
@@ -28,33 +34,35 @@ pub fn add_words(words: &mut [WordData], connection: &mut Connection) {
.prepare( .prepare(
"INSERT INTO words (key, value, tags, more, group_id) VALUES (?1, ?2, ?3, ?4, ?5)", "INSERT INTO words (key, value, tags, more, group_id) VALUES (?1, ?2, ?3, ?4, ?5)",
) )
.unwrap(); .expect("Failed to prepare statement");
for word in words.iter() { for word in words.iter() {
stmt.execute(params![ stmt.execute(params![
word.key, word.key,
word.value, word.value,
word.tags, word.tags,
serde_json::to_string(&word.additional).unwrap(), serde_json::to_string(&word.additional).unwrap_or_default(),
word.group_id word.group_id
]) ])
.unwrap(); .map_err(|_| DbSaveError)?;
} }
} }
let last_index: u32 = tx.last_insert_rowid() as u32; let last_index: u32 = tx.last_insert_rowid() as u32;
tx.commit().unwrap(); tx.commit().map_err(|_| DbSaveError)?;
let start_index = last_index - (count as u32) + 1; let start_index = last_index - (count as u32) + 1;
for (index, id) in (start_index..=last_index).enumerate() { for (index, id) in (start_index..=last_index).enumerate() {
words[index].id = id.into(); words[index].id = id.into();
} }
Ok(())
} }
pub fn update_word(word: &mut WordData, connection: &Connection) { pub fn update_word(word: &mut WordData, connection: &Connection) -> Result<(), NotificationData> {
if !word.id.is_valid() { if !word.id.is_valid() {
add_word(word, connection); Ok(add_word(word, connection)?)
} else { } else {
connection connection
.execute( .execute(
@@ -67,23 +75,25 @@ pub fn update_word(word: &mut WordData, connection: &Connection) {
&word.id, &word.id,
), ),
) )
.unwrap(); .map_err(|_| DbSaveError)?;
Ok(())
} }
} }
pub fn delete_word(word: &WordData, connection: &Connection) { pub fn delete_word(word: &WordData, connection: &Connection) -> Result<(), NotificationData> {
if !word.id.is_valid() { if !word.id.is_valid() {
return; return Err(DbSaveError);
} }
connection connection
.execute("DELETE FROM words WHERE id = ?1", (&word.id,)) .execute("DELETE FROM words WHERE id = ?1", (&word.id,))
.unwrap(); .map_err(|_| DbSaveError)?;
Ok(())
} }
pub fn load_words(connection: &Connection) -> Vec<WordData> { pub fn load_words(connection: &Connection) -> Result<Vec<WordData>, NotificationData> {
let mut stmt = connection let mut stmt = connection
.prepare("SELECT id, key, value, tags, more, group_id FROM words") .prepare("SELECT id, key, value, tags, more, group_id FROM words")
.unwrap(); .expect("Failed to prepare statement");
let word_iter = stmt let word_iter = stmt
.query_map([], |row| { .query_map([], |row| {
let additional: String = row.get(4)?; let additional: String = row.get(4)?;
@@ -92,24 +102,22 @@ pub fn load_words(connection: &Connection) -> Vec<WordData> {
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::<HashMap<String, String>>(&additional).unwrap(), additional: serde_json::from_str::<HashMap<String, String>>(&additional).unwrap_or_default(),
group_id: row.get(5)?, group_id: row.get(5)?,
}) })
}) })
.unwrap(); .map_err(|_| DbLoadError)?;
let mut buffer = vec![]; let mut buffer = Vec::with_capacity(1024);
for word in word_iter { word_iter.filter_map(|res| res.ok()).for_each(|i| buffer.push(i));
buffer.push(word.unwrap());
Ok(buffer)
} }
buffer pub fn load_word_groups(connection: &Connection) -> Result<Vec<WordGroup>, NotificationData> {
}
pub fn load_word_groups(connection: &Connection) -> Vec<WordGroup> {
let mut stmt = connection let mut stmt = connection
.prepare("SELECT id, name FROM word_group") .prepare("SELECT id, name FROM word_group")
.unwrap(); .expect("Failed to prepare statement");
let group_iter = stmt let group_iter = stmt
.query_map([], |row| { .query_map([], |row| {
Ok(WordGroup { Ok(WordGroup {
@@ -117,46 +125,50 @@ pub fn load_word_groups(connection: &Connection) -> Vec<WordGroup> {
name: row.get(1)?, name: row.get(1)?,
}) })
}) })
.unwrap(); .map_err(|_| DbLoadError)?;
let mut buffer = vec![]; let mut buffer = Vec::with_capacity(8);
for group in group_iter { group_iter.filter_map(|res| res.ok()).for_each(|i| buffer.push(i));
buffer.push(group.unwrap());
Ok(buffer)
} }
buffer pub fn add_group(group: &mut WordGroup, connection: &Connection) -> Result<(), NotificationData> {
}
pub fn add_group(group: &mut WordGroup, connection: &Connection) {
let index: u32 = connection let index: u32 = connection
.query_row( .query_row(
"INSERT INTO word_group (name) VALUES (?1) RETURNING id", "INSERT INTO word_group (name) VALUES (?1) RETURNING id",
(&group.name,), (&group.name,),
|row| row.get(0), |row| row.get(0),
) )
.unwrap(); .map_err(|_| DbSaveError)?;
group.id = index.into(); group.id = index.into();
Ok(())
} }
pub fn update_group(group: &mut WordGroup, connection: &Connection) { pub fn update_group(
group: &mut WordGroup,
connection: &Connection,
) -> Result<(), NotificationData> {
if !group.id.is_valid() { if !group.id.is_valid() {
add_group(group, connection); Ok(add_group(group, connection)?)
} else { } else {
connection connection
.execute( .execute(
"UPDATE word_group SET name = ?1 WHERE id = ?2", "UPDATE word_group SET name = ?1 WHERE id = ?2",
(&group.name, &group.id), (&group.name, &group.id),
) )
.unwrap(); .map_err(|_| DbSaveError)?;
Ok(())
} }
} }
pub fn delete_group(group: &WordGroup, connection: &Connection) { pub fn delete_group(group: &WordGroup, connection: &Connection) -> Result<(), NotificationData> {
if !group.id.is_valid() { if !group.id.is_valid() {
return; return Err(DbSaveError);
} }
connection connection
.execute("DELETE FROM word_group WHERE id = ?1", (&group.id,)) .execute("DELETE FROM word_group WHERE id = ?1", (&group.id,))
.unwrap(); .map_err(|_| DbSaveError)?;
Ok(())
} }
+28 -13
View File
@@ -1,12 +1,16 @@
use crate::app::AppState;
use crate::data::Id; use crate::data::Id;
use crate::data::entities::{WordData, WordGroup};
use crate::data_provider::words::{delete_group, delete_word, update_group, update_word}; use crate::data_provider::words::{delete_group, delete_word, update_group, update_word};
use crate::navigation::Page::{Import, Word};
use crate::navigation::{NavigatedPage, Page, RootMessage};
use crate::pages::dictionary::DictionaryMessage::*; use crate::pages::dictionary::DictionaryMessage::*;
use crate::pages::dictionary_quiz::DictionaryQuizState; use crate::pages::dictionary_quiz::DictionaryQuizState;
use crate::pages::import::ImportState; use crate::pages::import::ImportState;
use crate::navigation::Page::{Import, Word};
use crate::navigation::{NavigatedPage, Page, RootMessage};
use crate::styling::*;
use crate::pages::word::WordState; use crate::pages::word::WordState;
use crate::push_error;
use crate::push_error_task;
use crate::styling::*;
use chrono::{DateTime, TimeDelta, Utc}; use chrono::{DateTime, TimeDelta, Utc};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use iced::alignment::Vertical::Center; use iced::alignment::Vertical::Center;
@@ -17,14 +21,12 @@ use iced::widget::text_input::default;
use iced::widget::*; use iced::widget::*;
use iced::{Border, Color, Shadow, Task}; use iced::{Border, Color, Shadow, Task};
use iced_core::Length::Fill; use iced_core::Length::Fill;
use log::info;
use parking_lot::Mutex; use parking_lot::Mutex;
use rand::random_range; use rand::random_range;
use std::ops::Add; use std::ops::Add;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use log::info;
use crate::app::AppState;
use crate::data::entities::{WordData, WordGroup};
#[derive(Clone)] #[derive(Clone)]
pub struct DictionaryState { pub struct DictionaryState {
@@ -168,7 +170,9 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
let word = dict.remove(i); let word = dict.remove(i);
self.include_map.remove(i); self.include_map.remove(i);
self.auto_save_queue.remove(&i); self.auto_save_queue.remove(&i);
delete_word(&word, &state.connection) if let Err(err) = delete_word(&word, &state.connection) {
return push_error_task!(err);
}
} }
Include(i, b) => self.include_map[i] = b, Include(i, b) => self.include_map[i] = b,
IncludeTag(t, v) => { IncludeTag(t, v) => {
@@ -190,7 +194,7 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
self.search = s; self.search = s;
} }
SetTyping(b) => self.no_typing = b, SetTyping(b) => self.no_typing = b,
SubmitWord(i) => self.save_word(i), SubmitWord(i) => return self.save_word(i),
Back => {} Back => {}
Test => {} Test => {}
CreateGroup => { CreateGroup => {
@@ -213,7 +217,10 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
let connection = &state.connection; let connection = &state.connection;
let mut group = state.word_groups[self.selected_group_index].clone(); let mut group = state.word_groups[self.selected_group_index].clone();
update_group(&mut group, connection); if let Err(err) = update_group(&mut group, connection) {
return push_error_task!(err);
}
state.word_groups[self.selected_group_index] = group; state.word_groups[self.selected_group_index] = group;
} }
SelectGroup(i) => { SelectGroup(i) => {
@@ -236,7 +243,10 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
let remove_group_id = group.id; let remove_group_id = group.id;
let connection = &state.connection; let connection = &state.connection;
delete_group(group, connection); if let Err(err) = delete_group(group, connection) {
return push_error_task!(err);
}
state.word_groups.remove(self.selected_group_index); state.word_groups.remove(self.selected_group_index);
state state
.dictionary .dictionary
@@ -257,7 +267,8 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
} }
if self.auto_save_queue[&word_index] <= now { if self.auto_save_queue[&word_index] <= now {
self.auto_save_queue.remove(&word_index); self.auto_save_queue.remove(&word_index);
self.save_word(word_index);
return self.save_word(word_index);
} }
} }
ToImport => {} ToImport => {}
@@ -309,13 +320,17 @@ impl DictionaryState {
result result
} }
fn save_word(&mut self, i: usize) { fn save_word(&mut self, i: usize) -> Task<RootMessage> {
let state = &mut self.state.lock(); let state = &mut self.state.lock();
let connection = &state.connection; let connection = &state.connection;
let word = &mut state.dictionary[i].clone(); let word = &mut state.dictionary[i].clone();
update_word(word, connection); if let Err(err) = update_word(word, connection) {
return push_error_task!(err);
}
state.dictionary[i] = word.clone(); state.dictionary[i] = word.clone();
Task::none()
} }
fn launch_auto_save_offset(&mut self, index: usize) -> Task<RootMessage> { fn launch_auto_save_offset(&mut self, index: usize) -> Task<RootMessage> {
+2 -2
View File
@@ -444,7 +444,7 @@ impl ImportState {
}; };
{ {
let state = state.lock(); let state = state.lock();
add_group(&mut group_entity, &state.connection); add_group(&mut group_entity, &state.connection)?;
} }
let group_id = group_entity.id; let group_id = group_entity.id;
@@ -484,7 +484,7 @@ impl ImportState {
let mut index = 0; let mut index = 0;
let total_len = words_list.len() as f32 / 1024.0; let total_len = words_list.len() as f32 / 1024.0;
for word in &mut words_list.chunks_mut(1024) { for word in &mut words_list.chunks_mut(1024) {
add_words(word, connection); add_words(word, connection)?;
index += 1; index += 1;
let mut progress = progress.lock(); let mut progress = progress.lock();
*progress = (index as f32 / total_len).min(1.0); *progress = (index as f32 / total_len).min(1.0);
+12 -5
View File
@@ -1,15 +1,17 @@
use crate::data_provider::words::{delete_word, update_word}; use crate::app::AppState;
use crate::data::entities::WordData; use crate::data::entities::WordData;
use crate::data_provider::words::{delete_word, update_word};
use crate::navigation::Page::PreviousPage; use crate::navigation::Page::PreviousPage;
use crate::navigation::{NavigatedPage, Page, RootMessage}; use crate::navigation::{NavigatedPage, Page, RootMessage};
use crate::styling::*;
use crate::pages::word::WordMessage::*; use crate::pages::word::WordMessage::*;
use crate::push_error;
use crate::push_error_task;
use crate::styling::*;
use iced::widget::button::danger; use iced::widget::button::danger;
use iced::widget::{button, column, row, rule, scrollable, text, text_input}; use iced::widget::{button, column, row, rule, scrollable, text, text_input};
use iced::{Element, Fill, Task}; use iced::{Element, Fill, Task};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::Arc; use std::sync::Arc;
use crate::app::AppState;
#[derive(Clone)] #[derive(Clone)]
pub struct WordState { pub struct WordState {
@@ -34,13 +36,18 @@ impl NavigatedPage<WordMessage> for WordState {
Save => { Save => {
let mut state = self.state.lock(); let mut state = self.state.lock();
state.dictionary[self.index] = self.word.clone(); state.dictionary[self.index] = self.word.clone();
update_word(&mut self.word, &state.connection);
if let Err(err) = update_word(&mut self.word, &state.connection) {
return push_error_task!(err);
}
return Task::done(RootMessage::Word(Back)); return Task::done(RootMessage::Word(Back));
} }
Delete => { Delete => {
let mut state = self.state.lock(); let mut state = self.state.lock();
state.dictionary.remove(self.index); state.dictionary.remove(self.index);
delete_word(&self.word, &state.connection); if let Err(err) = delete_word(&self.word, &state.connection) {
return push_error_task!(err);
}
return Task::done(RootMessage::Word(Back)); return Task::done(RootMessage::Word(Back));
} }
SetTags(n) => self.word.tags = n, SetTags(n) => self.word.tags = n,