Migrate to Id type

This commit is contained in:
2026-08-18 12:47:06 +03:00
parent 99b3818181
commit 97c76350e1
12 changed files with 106 additions and 62 deletions
+4 -4
View File
@@ -1,4 +1,4 @@
use crate::lang::{DeckSettings, OrderMode};
use crate::lang::{DeckSettings, OrderMode, INVALID_ID};
use rusqlite::Connection;
@@ -43,11 +43,11 @@ pub fn add_set(set: &mut DeckSettings, connection: &Connection) {
)
.unwrap_or_else(|e| {println!("{}", e); 0});
set.id = index;
set.id = index.into();
}
pub fn update_card_set(set: &mut DeckSettings, connection: &Connection) {
if set.id == 0 {
if set.id == INVALID_ID {
add_set(set, connection);
} else {
connection
@@ -66,7 +66,7 @@ pub fn update_card_set(set: &mut DeckSettings, connection: &Connection) {
}
pub fn delete_set(set: &DeckSettings, connection: &Connection) {
if set.id == 0 {
if set.id == INVALID_ID {
return;
}
connection
+3 -3
View File
@@ -1,4 +1,4 @@
use crate::lang::{DeckSettings, CardStatistics};
use crate::lang::{DeckSettings, CardStatistics, INVALID_ID};
use rusqlite::Connection;
use std::time::Instant;
@@ -62,7 +62,7 @@ pub fn add_stat_list(stat: &mut [CardStatistics], connection: &Connection) {
let start_index = last_index - (stat.len() as u32) + 1;
for (index, id) in (start_index..=last_index).enumerate() {
stat[index].id = id;
stat[index].id = id.into();
}
println!("Added {} cards for {:?}", stat.len(), time.elapsed());
@@ -103,7 +103,7 @@ pub fn update_stat_score(stat: &CardStatistics, connection: &Connection) {
}
pub fn delete_stat(stat: &CardStatistics, connection: &Connection) {
if stat.id == 0 {
if stat.id == INVALID_ID {
return;
}
connection
+5 -5
View File
@@ -1,5 +1,5 @@
use crate::dictionary::app_data_dir;
use crate::lang::WordOpenMode;
use crate::lang::{Id, WordOpenMode};
use chrono::{DateTime, Utc};
use std::fs;
use std::fs::{File, OpenOptions};
@@ -7,7 +7,7 @@ use std::io::Write;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
pub fn get_history_of_set(id: u32) -> Vec<HistoryItem> {
pub fn get_history_of_set(id: Id) -> Vec<HistoryItem> {
let app_dir = history_dir();
let head = app_dir.clone().join(format!("set_{}_history.csv", id));
let mut lines = Vec::new();
@@ -32,7 +32,7 @@ fn parse_history_items(strings: Vec<String>) -> Vec<HistoryItem> {
if let [time, word, mode, before, after] = string.split(';').collect::<Vec<&str>>()[..] {
items.push(HistoryItem {
timestamp: DateTime::from_timestamp(time.parse().unwrap(), 0).unwrap(),
word_id: word.parse::<u32>().unwrap(),
word_id: word.parse::<u32>().unwrap().into(),
mode: match mode.parse::<u8>().unwrap() {
2 => WordOpenMode::Hard,
3 => WordOpenMode::Ok,
@@ -48,7 +48,7 @@ fn parse_history_items(strings: Vec<String>) -> Vec<HistoryItem> {
items
}
pub fn push_note(set_id: u32, item: HistoryItem) {
pub fn push_note(set_id: Id, item: HistoryItem) {
let app_dir = history_dir();
let path = app_dir.clone().join(format!("set_{}_history.csv", set_id));
@@ -84,7 +84,7 @@ pub fn history_dir() -> PathBuf {
#[derive(Clone)]
pub struct HistoryItem {
pub timestamp: DateTime<Utc>,
pub word_id: u32,
pub word_id: Id,
pub mode: WordOpenMode,
pub before: u8,
pub after: u8,
+8 -8
View File
@@ -1,4 +1,4 @@
use crate::lang::{WordData, WordGroup};
use crate::lang::{WordData, WordGroup, INVALID_ID};
use rusqlite::{Connection, params};
use std::collections::HashMap;
@@ -20,7 +20,7 @@ pub fn add_word(word: &mut WordData, connection: &Connection) {
0
});
word.id = index;
word.id = index.into();
}
pub fn add_words(words: &mut [WordData], connection: &mut Connection) {
@@ -59,12 +59,12 @@ pub fn add_words(words: &mut [WordData], connection: &mut Connection) {
let start_index = last_index - (count as u32) + 1;
for (index, id) in (start_index..=last_index).enumerate() {
words[index].id = id;
words[index].id = id.into();
}
}
pub fn update_word(word: &mut WordData, connection: &Connection) {
if word.id == 0 {
if word.id == INVALID_ID {
add_word(word, connection);
} else {
connection
@@ -86,7 +86,7 @@ pub fn update_word(word: &mut WordData, connection: &Connection) {
}
pub fn delete_word(word: &WordData, connection: &Connection) {
if word.id == 0 {
if word.id == INVALID_ID {
return;
}
connection
@@ -156,11 +156,11 @@ pub fn add_group(group: &mut WordGroup, connection: &Connection) {
0
});
group.id = index;
group.id = index.into();
}
pub fn update_group(group: &mut WordGroup, connection: &Connection) {
if group.id == 0 {
if group.id == INVALID_ID {
add_group(group, connection);
} else {
connection
@@ -176,7 +176,7 @@ pub fn update_group(group: &mut WordGroup, connection: &Connection) {
}
pub fn delete_group(group: &WordGroup, connection: &Connection) {
if group.id == 0 {
if group.id == INVALID_ID {
return;
}
connection
+8 -8
View File
@@ -2,7 +2,7 @@ use crate::data_provider::words::{delete_group, delete_word, update_group, updat
use crate::dictionary::DictionaryMessage::*;
use crate::dictionary_test::DictionaryQuizState;
use crate::import::ImportState;
use crate::lang::{WordData, WordGroup};
use crate::lang::{WordData, WordGroup, INVALID_ID};
use crate::navigation::Page::{Import, Word};
use crate::navigation::{NavigatedPage, Page};
use crate::styling::*;
@@ -96,7 +96,7 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
let dict = &state.dictionary;
word = dict[*index].clone();
}
if word.id != 0 {
if word.id != INVALID_ID {
return Some(Word(WordState::new(word, *index, self.state.clone())));
}
}
@@ -172,7 +172,7 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
}
Include(i, b) => self.include_map[i] = b,
IncludeTag(t, v) => {
let index: u32;
let index;
{
let state = self.state.lock().unwrap();
index = state.word_groups[self.selected_group_index].id;
@@ -197,7 +197,7 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
let state = &mut self.state.lock().unwrap();
state.word_groups.push(WordGroup {
id: 0,
id: 0.into(),
name: format!("Группа слов {}", random_range(100..1000)),
});
}
@@ -222,7 +222,7 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
}
SelectGroup(i) => {
self.selected_group_index = i;
let index: u32;
let index;
{
let state = self.state.lock().unwrap();
index = state.word_groups[i].id;
@@ -423,7 +423,7 @@ impl DictionaryState {
let line_button = || {
let action = WordAction(index);
if data.id == 0 {
if data.id == INVALID_ID {
return button("-").on_press(action).style(|_x, _status| Style {
background: None,
text_color: Color::BLACK,
@@ -532,7 +532,7 @@ impl DictionaryState {
});
}
fn update_words_include(&mut self, group_id: u32) {
fn update_words_include(&mut self, group_id: crate::lang::Id) {
let include_tags = self
.tag_map
.iter()
@@ -626,6 +626,6 @@ struct WordLineState {
key: String,
value: String,
tags: String,
id: u32,
id: crate::lang::Id,
index: usize,
}
+1 -1
View File
@@ -46,7 +46,7 @@ impl NavigatedPage<HistoryMessage> for HistoryState {
}
impl HistoryState {
pub fn new(id: u32, state: Arc<Mutex<AppState>>) -> Self {
pub fn new(id: crate::lang::Id, state: Arc<Mutex<AppState>>) -> Self {
let state = state.lock().unwrap();
let history = get_history_of_set(id);
let words = history
+1 -1
View File
@@ -446,7 +446,7 @@ impl ImportState {
let map_indices = Self::get_mapping_indices(&group);
let mut group_entity = WordGroup {
id: 0,
id: 0.into(),
name: group.name.clone(),
};
{
+59 -15
View File
@@ -11,8 +11,11 @@ use rayon::iter::IndexedParallelIterator;
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use rhai::{Engine, Scope};
use serde::{Deserialize, Serialize};
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef};
use rusqlite::ToSql;
use std::cmp::PartialEq;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::sync::{Arc, Mutex};
use std::time::Instant;
@@ -20,6 +23,9 @@ const MAX_HISTORY_LEN: usize = 20;
const MAX_HISTORY_LEN_PART: f32 = 0.33;
const MAX_SCORE: u8 = 25;
const FADE_PER_DAY: f32 = 0.95;
#[derive(Clone, PartialEq, Eq, Debug, Hash, Copy)]
pub(crate) struct Id(u32);
pub const INVALID_ID: Id = Id(0);
#[derive(Clone, Debug)]
pub struct KanaSet {
name: String,
@@ -225,40 +231,40 @@ impl PartialEq<Self> for KanaSet {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug)]
pub struct WordData {
pub id: u32,
pub id: Id,
pub key: String,
pub value: String,
pub tags: String,
pub additional: HashMap<String, String>,
pub group_id: u32,
pub group_id: Id,
}
impl WordData {
pub fn new() -> Self {
Self {
id: 0,
id: 0.into(),
key: String::new(),
value: String::new(),
tags: String::new(),
additional: Default::default(),
group_id: 1,
group_id: 1.into(),
}
}
}
#[derive(Clone)]
pub struct WordGroup {
pub id: u32,
pub id: Id,
pub name: String,
}
#[derive(Clone, PartialEq)]
pub struct CardStatistics {
pub id: u32,
pub word_id: u32,
pub set_id: u32,
pub id: Id,
pub word_id: Id,
pub set_id: Id,
pub last_open: DateTime<Utc>,
pub score: u8,
}
@@ -320,7 +326,7 @@ impl DeckData {
.map(|w| state_locked.dictionary.get(*w).unwrap())
.cloned()
.collect();
let word_ids = last_list.iter().map(|l| l.id).collect::<Vec<u32>>();
let word_ids = last_list.iter().map(|l| l.id).collect::<Vec<Id>>();
let mut index = 0;
for stat in current_set.clone() {
@@ -410,7 +416,7 @@ impl DeckData {
self.settings.id,
HistoryItem {
timestamp: Utc::now(),
word_id: word.word_id,
word_id: word.word_id.into(),
mode: WordOpenMode::Easy,
before: old_score,
after: new_score,
@@ -606,7 +612,7 @@ impl WorstWordsSRSModule {
#[derive(Clone)]
pub struct DeckSettings {
pub id: u32,
pub id: Id,
pub name: String,
pub forward: String,
pub backward: String,
@@ -619,7 +625,7 @@ pub struct DeckSettings {
impl DeckSettings {
pub(crate) fn with_name(name: String) -> DeckSettings {
DeckSettings {
id: 0,
id: 0.into(),
name,
forward: "".to_string(),
backward: "".to_string(),
@@ -661,7 +667,7 @@ impl DeckSettings {
}
let mut scope = Scope::new();
scope
.push_constant("id", word.id)
.push_constant("id", word.id.0)
.push_constant("key", word.key.clone())
.push_constant("value", word.value.clone())
.push_constant("tags", word.tags.clone())
@@ -691,3 +697,41 @@ impl DeckSettings {
self.forward == "speech" || self.backward == "speech"
}
}
impl Into<Id> for u32{
fn into(self) -> Id {
Id(self)
}
}
// impl From<u32> for Id{
// fn from(id: u32) -> Id{
// Id(id)
// }
// }
impl Into<u32> for Id{
fn into(self) -> u32 {
self.0
}
}
impl Display for Id {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl FromSql for Id{
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
match value {
ValueRef::Integer(i) => Ok(Id(i as u32)),
_ => Err(FromSqlError::InvalidType),
}
}
}
impl ToSql for Id{
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.0 as i64))
}
}
+2 -2
View File
@@ -23,7 +23,7 @@ use crate::data_provider::card_sets::load_sets;
use crate::data_provider::settings::get_setting;
use crate::data_provider::sqlite::default_connection;
use crate::data_provider::words::{load_word_groups, load_words};
use crate::lang::{DeckSettings, WordData, WordGroup};
use crate::lang::{DeckSettings, Id, WordData, WordGroup};
use crate::navigation::{AppSettings, RootMessage, ScreenState};
use crate::quiz::*;
use chrono::NaiveDate;
@@ -85,7 +85,7 @@ pub struct AppState {
pub word_groups: Vec<WordGroup>,
pub connection: Connection,
pub sync_data: AppSettings,
pub activity: HashMap<u32, Vec<(NaiveDate, u32)>>,
pub activity: HashMap<Id, Vec<(NaiveDate, u32)>>,
}
impl Default for AppState {
+4 -3
View File
@@ -31,6 +31,7 @@ use hashbrown::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Instant;
use rusqlite::Connection;
use crate::lang::Id;
impl Default for ScreenState {
fn default() -> Self {
@@ -60,7 +61,7 @@ pub enum RootMessage {
RepetitionSettings(RepetitionSettingsMessage),
Import(ImportMessage),
Keyboard(Event),
DataLoaded(HashMap<u32, Vec<(NaiveDate, u32)>>),
DataLoaded(HashMap<Id, Vec<(NaiveDate, u32)>>),
None,
UpdateData,
}
@@ -126,9 +127,9 @@ impl ScreenState {
for file in directory.read_dir().unwrap().flatten() {
let mut vec = vec![];
let history_file_name = file.file_name().into_string().unwrap();
let id = history_file_name[4..history_file_name.len() - 12]
let id : Id = history_file_name[4..history_file_name.len() - 12]
.parse::<u32>()
.unwrap();
.unwrap().into();
let history = get_history_of_set(id);
let by_date = history.chunk_by(|x, x1| {
+2 -1
View File
@@ -13,6 +13,7 @@ use iced::{Element, Fill, Task, Theme, alignment, keyboard};
use rodio::MixerDeviceSink;
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use crate::lang::Id;
use tokio::task::spawn_blocking;
pub struct RepetitionState {
@@ -24,7 +25,7 @@ pub struct RepetitionState {
open: bool,
can_play: bool,
sink: Arc<MixerDeviceSink>,
opened: HashSet<u32>,
opened: HashSet<Id>,
}
impl NavigatedPage<RepetitionMessage> for RepetitionState {
+8 -10
View File
@@ -27,14 +27,12 @@ use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct RepetitionsState {
selected_set_index: Option<usize>,
correct_filters: Vec<bool>,
current_sets_cards_cache: HashMap<usize, (Vec<usize>, Vec<usize>)>,
word_id_index_map: HashMap<u32, usize>,
local_settings: HashMap<u32, DeckLocalSetting>,
view_data: Option<SetViewData>,
pub state: Arc<Mutex<AppState>>,
set_names: Vec<String>
state: Arc<Mutex<AppState>>,
}
impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
@@ -89,7 +87,9 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
GoToHistory => {}
GoToSettings => {}
CreateSet => {}
CreateSet => {
}
DeleteSet => {}
SetName(_) => {}
SelectSet(_) => {}
@@ -209,15 +209,15 @@ impl RepetitionsState {
button("Проверить фильтр")
.style(jl_button)
.on_press(TryFilter),
self.count_view(&set),
self.count_view(set),
]
.spacing(QUARTER_SPACING),
]
.spacing(DEFAULT_SPACING)
} else {
column![
self.filled_set_data_view(&set),
self.word_append_panel(&set),
self.filled_set_data_view(set),
self.word_append_panel(set),
radio(
"Обычный режим",
OrderMode::Default,
@@ -430,7 +430,6 @@ impl RepetitionsState {
impl RepetitionsState {
fn append_all_words(&self, set: &DeckSettings) {
let index = self.selected_set_index.unwrap();
let cache = &self.current_sets_cards_cache[&index];
let mut created_set = HashSet::with_capacity(cache.0.len());
cache.0.iter().for_each(|c| {
@@ -461,13 +460,12 @@ impl RepetitionsState {
fn select_set(&mut self, index: usize) {
let state = self.state.lock().unwrap();
if let Some(set) = state.card_sets.get(index) {
self.selected_set_index = Some(index);
}
}
fn clear_selection(&mut self) {
self.selected_set_index = None;
self.view_data = None;
}
}
#[derive(Clone)]