diff --git a/src/data/entities.rs b/src/data/entities.rs index 401e5b3..f46bcb0 100644 --- a/src/data/entities.rs +++ b/src/data/entities.rs @@ -20,6 +20,17 @@ pub struct WordData { pub additional: HashMap, pub group_id: Id, } + +impl EntityData for WordData { + fn id(&self) -> Id { + self.id + } + + fn set_id(&mut self, id: Id) { + self.id = id; + } +} + #[derive(Clone)] pub struct WordGroup { pub id: Id, @@ -168,49 +179,15 @@ impl CardStatistics { } } -#[derive(Clone)] -pub struct Entity> { - pub(crate) repo: R, +#[derive(Clone, Copy)] +pub struct EntityHandle { pub(crate) _phantom: PhantomData, - pub(crate) inner: EntityInner, -} - -#[derive(Clone)] -pub(crate) struct EntityInner { pub(crate) id: Id, pub(crate) real: bool, + pub(crate) index: u32, } -impl> Entity { - pub fn load(&self) -> EntityAccess { - let clone = *self.clone(); - let repo = &self.repo; - let value = repo.get(self.inner.id).unwrap(); - EntityAccess { value, entity: clone, } - } -} - -pub struct EntityAccess> { - entity: Entity, - value: E, -} - -impl> Deref for EntityAccess { - type Target = E; - - fn deref(&self) -> &Self::Target { - &self.value - } -} - -impl> DerefMut for EntityAccess { - fn deref_mut(&mut self) -> &mut E { - &mut self.value - } -} - -pub trait Repository { - fn get(&self, id: Id) -> Option; - fn create(&mut self, new: T) -> Entity>; - fn save(&mut self, entity: EntityInner) -> MaybeError; +pub trait EntityData{ + fn id(&self) -> Id; + fn set_id(&mut self, id: Id); } diff --git a/src/data/repositories.rs b/src/data/repositories.rs index dad8999..ee3fa58 100644 --- a/src/data/repositories.rs +++ b/src/data/repositories.rs @@ -1,46 +1,272 @@ use crate::data::Id; -use crate::data::entities::{Entity, EntityInner, Repository, WordData}; +use crate::data::entities::{EntityData, EntityHandle, WordData}; use crate::notification::MaybeError; use parking_lot::{Mutex, RwLock}; use rusqlite::Connection; use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; use std::sync::Arc; +use iced::application::IntoBoot; +use rusqlite::fallible_iterator::Iterator; + +pub trait Repository where D : DbLayer { + fn get(&self, handle: &mut EntityHandle) -> Option; + fn get_ref(&self, handle: &mut EntityHandle) -> Option<&T>; + fn get_mut(&mut self, handle: &mut EntityHandle) -> Option<&mut T>; + fn handle_by_id(&self, id: Id) -> EntityHandle; + fn create(&mut self, new: T) -> EntityHandle; + fn save(&mut self, entity: &mut EntityHandle) -> MaybeError; + fn delete(&mut self, handle: EntityHandle) -> MaybeError; + fn init(&mut self) -> MaybeError; + fn reload(&mut self) -> MaybeError; +} + +pub trait BatchedRepository where D : BatchedDbLayer, T: EntityData +{ + fn save_many(&mut self, handles: &mut [EntityHandle]) -> MaybeError; + fn delete_many(&mut self, handles: &mut [EntityHandle]) -> MaybeError; + +} + +pub trait DbLayer { + fn write_entity(&self, entity: &T, connection: &Connection) -> MaybeError; + fn add_entity(&mut self, entity: &mut T, connection: &Connection) -> MaybeError; + fn delete_entity(&mut self, id: Id, connection: &Connection) -> MaybeError; + fn load_all(&self, storage: &mut Vec, connection: &Connection) -> MaybeError; +} + +pub trait BatchedDbLayer where T : EntityData{ + fn delete_many(&mut self, ids: &[Id], connection: &Connection) -> MaybeError; + fn add_many_entity(&mut self, entity: &mut [T], connection: &Connection) -> MaybeError; +} + #[derive(Clone)] -pub struct WordRepository { - inner: Arc>, +pub struct DataRepository { + inner: Arc>>, } -pub struct WordRepositoryInner { - cache: Vec, - temporal: Vec>, +pub struct DataRepositoryInner { + cache: Vec, + temporal: Vec, next_temporal: u32, connection: Arc>, + data_layer: D, } -impl Repository for WordRepository { - fn get(&self, id: Id) -> Option { - let inner = self.inner.read(); - Some(inner.cache[inner.cache.binary_search_by(|w| w.id.cmp(&id)).ok()?].clone()) +impl Deref for DataRepository { + type Target = Arc>>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl> Repository for DataRepositoryInner { + fn get(&self, handle: &mut EntityHandle) -> Option { + let slice = if handle.real { + self.cache.as_slice() + } else { + self.temporal.as_slice() + }; + let index = fast_position_of(slice, handle)?; + Some(slice[index].clone()) } - fn create(&mut self, new: WordData) -> Entity { - let mut inner = self.inner.write(); - let next_temporal = inner.next_temporal; - inner.next_temporal += 1; - drop(inner); - let entity = Entity { - inner: EntityInner { - id: next_temporal.into(), - real: false, - }, - repo: self.clone(), + fn get_ref(&self, handle: &mut EntityHandle) -> Option<&T> { + let slice = if handle.real { + self.cache.as_slice() + } else { + self.temporal.as_slice() + }; + let index = fast_position_of(slice, handle)?; + Some(&slice[index]) + } + + fn get_mut(&mut self, handle: &mut EntityHandle) -> Option<&mut T> { + let slice = if handle.real { + self.cache.as_mut_slice() + } else { + self.temporal.as_mut_slice() + }; + let index = fast_position_of(slice, handle)?; + Some(&mut slice[index]) + } + + fn handle_by_id(&self, id: Id) -> EntityHandle { + EntityHandle{ + _phantom: Default::default(), + id, + real: true, + index: id.0, + } + } + + // fn get_static(&self, mut handle: EntityHandle) -> Option { + // let slice = if handle.real { + // self.cache.as_slice() + // } else { + // self.temporal.as_slice() + // }; + // let index = fast_position_of(slice, &mut handle)?; + // Some(slice[index].clone()) + // } + // + // fn get_ref_static(&self, mut handle: EntityHandle) -> Option<&T> { + // let slice = if handle.real { + // self.cache.as_slice() + // } else { + // self.temporal.as_slice() + // }; + // let index = fast_position_of(slice, &mut handle)?; + // Some(&slice[index]) + // } + // + // fn get_mut_static(&mut self, mut handle: EntityHandle) -> Option<&mut T> { + // let slice = if handle.real { + // self.cache.as_mut_slice() + // } else { + // self.temporal.as_mut_slice() + // }; + // let index = fast_position_of(slice, &mut handle)?; + // Some(&mut slice[index]) + // } + + fn create(&mut self, mut new: T) -> EntityHandle { + let next_temporal = self.next_temporal; + self.next_temporal += 1; + let entity = EntityHandle { + id: next_temporal.into(), + real: false, + index: self.temporal.len() as u32, _phantom: PhantomData, }; + new.set_id(entity.id); + self.temporal.push(new); entity } - fn save(&mut self, entity: EntityInner) -> MaybeError { - todo!() + fn save(&mut self, entity: &mut EntityHandle) -> MaybeError { + if entity.real { + let data = self.get_ref(entity).unwrap(); + self.data_layer.write_entity(data, &self.connection.lock())?; + } else { + let index = fast_position_of(self.temporal.as_slice(), entity).unwrap(); + let mut value = self.temporal.remove(index); + self.data_layer.add_entity(&mut value, &self.connection.lock())?; + entity.real = true; + entity.id = value.id(); + entity.index = self.cache.len() as u32; + self.cache.push(value); + } + + Ok(()) + } + + fn delete(&mut self, mut handle: EntityHandle) -> MaybeError { + + let vec = if handle.real { + self.data_layer.delete_entity(handle.id, &self.connection.lock())?; + &mut self.cache + } else { + &mut self.temporal + }; + + let index = fast_position_of(vec.as_slice(), &mut handle).unwrap(); + vec.remove(index); + Ok(()) + } + + fn init(&mut self) -> MaybeError { + self.data_layer.load_all(&mut self.cache, &self.connection.lock())?; + Ok(()) + } + + fn reload(&mut self) -> MaybeError { + self.cache.clear(); + self.temporal.clear(); + self.init()?; + Ok(()) } } +impl> BatchedRepository for DataRepositoryInner { + fn save_many(&mut self, handles: &mut [EntityHandle]) -> MaybeError { + todo!() + } + + fn delete_many(&mut self, handles: &mut [EntityHandle]) -> MaybeError { + let mut temp = Vec::with_capacity(handles.len() / 2); + let mut cached = Vec::with_capacity(handles.len() / 2); + let mut cell = 0; + handles.sort_by(|a, b| {a.id.cmp(&b.id)}); + handles.iter().for_each(|handle| if handle.real { cached.push(handle.id)} else { + temp.push(handle.id) + }); + + let len = temp.len(); + self.temporal.retain(|x| { + if cell == len { return true; } + let find = temp[cell]; + let remove = x.id() == find; + if remove { cell += 1; } + !remove + }); + self.data_layer.delete_many(cached.as_slice(), &self.connection.lock())?; + let len = cached.len(); + cell = 0; + self.cache.retain(|x| { + if cell == len { return true; } + let find = cached[cell]; + let remove = x.id() == find; + if remove { cell += 1; } + !remove + }); + + Ok(()) + } +} + +pub fn fast_position_of( + storage: &[E], + handle: &mut EntityHandle, +) -> Option { + if storage.is_empty() { + return None; + } + + let max_idx = storage.len() - 1; + let upper = (handle.index as usize).min(max_idx); + let peek = storage[upper].id(); + + if peek == handle.id { + handle.index = upper as u32; + return Some(upper); + } + + if peek.0 < handle.id.0 { + return None; + } + + let target_id_val = handle.id.0 as i64; + let peek_val = peek.0 as i64; + let delta = peek_val - target_id_val; + + let new_index = (upper as i64 - delta).max(0) as usize; + + if storage[new_index].id() == handle.id { + handle.index = new_index as u32; + return Some(new_index); + } + + if new_index == upper { + return None; + } + + let index = storage[new_index..upper] + .binary_search_by(|e| e.id().cmp(&handle.id)) + .ok()? + + new_index; + + handle.index = index as u32; + Some(index) +} diff --git a/src/main.rs b/src/main.rs index 34b8108..58e8c7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ mod navigation; pub mod notification; mod pages; mod styling; +pub mod tests; use crate::app::start; use mimalloc::MiMalloc; diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..db4efda --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,92 @@ +#[cfg(test)] +mod mod_tests { + use crate::data::Id; + use crate::data::entities::{EntityData, EntityHandle}; + use crate::data::repositories::fast_position_of; + + struct ProtoEntity { + id: Id, + _data: i128, + } + impl EntityData for ProtoEntity { + fn id(&self) -> Id { + self.id + } + + fn set_id(&mut self, id: Id) { + self.id = id; + } + } + + impl ProtoEntity { + fn new(n: i128) -> Self { + Self { + id: (n as u32).into(), + _data: n, + } + } + } + + #[test] + fn search_direct() { + let mut entity = EntityHandle { + _phantom: Default::default(), + id: 8.into(), + real: true, + index: 3, + }; + assert_eq!( + fast_position_of(&[1, 2, 3, 8, 9, 12].map(ProtoEntity::new), &mut entity), + Some(3) + ); + } + + #[test] + fn search_0() { + let mut entity = EntityHandle { + _phantom: Default::default(), + id: 8.into(), + real: true, + index: 4, + }; + assert_eq!( + fast_position_of(&[1, 2, 3, 8, 9, 12].map(ProtoEntity::new), &mut entity), + Some(3) + ); + } + + #[test] + fn search_1() { + let mut entity = EntityHandle { + _phantom: Default::default(), + id: 9.into(), + real: true, + index: 5, + }; + assert_eq!( + fast_position_of(&[1, 2, 3, 8, 9, 12].map(ProtoEntity::new), &mut entity), + Some(1) + ); + } + + #[test] + fn search_2() { + let mut entity = EntityHandle { + _phantom: Default::default(), + id: 106.into(), + real: true, + index: 5, + }; + assert_eq!( + fast_position_of( + &[ + 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 21, 22, 23, 105, 106, 107, + 108, 109, 110 + ] + .map(ProtoEntity::new), + &mut entity + ), + Some(1) + ); + } +}