More repo code
This commit is contained in:
1 parent
807f398d81
commit
d99fd337da
4 files changed
+357
-61
No files matched your search
+17
-40
@@ -20,6 +20,17 @@ pub struct WordData {
|
|||||||
pub additional: HashMap<String, String>,
|
pub additional: HashMap<String, String>,
|
||||||
pub group_id: Id,
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct WordGroup {
|
pub struct WordGroup {
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
@@ -168,49 +179,15 @@ impl CardStatistics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Entity<E, R: Repository<E>> {
|
pub struct EntityHandle<E> {
|
||||||
pub(crate) repo: R,
|
|
||||||
pub(crate) _phantom: PhantomData<E>,
|
pub(crate) _phantom: PhantomData<E>,
|
||||||
pub(crate) inner: EntityInner,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct EntityInner {
|
|
||||||
pub(crate) id: Id,
|
pub(crate) id: Id,
|
||||||
pub(crate) real: bool,
|
pub(crate) real: bool,
|
||||||
|
pub(crate) index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, R: Repository<E>> Entity<E, R> {
|
pub trait EntityData{
|
||||||
pub fn load(&self) -> EntityAccess<E, R> {
|
fn id(&self) -> Id;
|
||||||
let clone = *self.clone();
|
fn set_id(&mut self, id: Id);
|
||||||
let repo = &self.repo;
|
|
||||||
let value = repo.get(self.inner.id).unwrap();
|
|
||||||
EntityAccess { value, entity: clone, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EntityAccess<E, R: Repository<E>> {
|
|
||||||
entity: Entity<E, R>,
|
|
||||||
value: E,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, R: Repository<E>> Deref for EntityAccess<E, R> {
|
|
||||||
type Target = E;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, R: Repository<E>> DerefMut for EntityAccess<E, R> {
|
|
||||||
fn deref_mut(&mut self) -> &mut E {
|
|
||||||
&mut self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Repository<T> {
|
|
||||||
fn get(&self, id: Id) -> Option<T>;
|
|
||||||
fn create(&mut self, new: T) -> Entity<T, impl Repository<T>>;
|
|
||||||
fn save(&mut self, entity: EntityInner) -> MaybeError;
|
|
||||||
}
|
}
|
||||||
+247
-21
@@ -1,46 +1,272 @@
|
|||||||
use crate::data::Id;
|
use crate::data::Id;
|
||||||
use crate::data::entities::{Entity, EntityInner, Repository, WordData};
|
use crate::data::entities::{EntityData, EntityHandle, WordData};
|
||||||
use crate::notification::MaybeError;
|
use crate::notification::MaybeError;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use iced::application::IntoBoot;
|
||||||
|
use rusqlite::fallible_iterator::Iterator;
|
||||||
|
|
||||||
|
pub trait Repository<T, D> where D : DbLayer<T> {
|
||||||
|
fn get(&self, handle: &mut EntityHandle<T>) -> Option<T>;
|
||||||
|
fn get_ref(&self, handle: &mut EntityHandle<T>) -> Option<&T>;
|
||||||
|
fn get_mut(&mut self, handle: &mut EntityHandle<T>) -> Option<&mut T>;
|
||||||
|
fn handle_by_id(&self, id: Id) -> EntityHandle<T>;
|
||||||
|
fn create(&mut self, new: T) -> EntityHandle<T>;
|
||||||
|
fn save(&mut self, entity: &mut EntityHandle<T>) -> MaybeError;
|
||||||
|
fn delete(&mut self, handle: EntityHandle<T>) -> MaybeError;
|
||||||
|
fn init(&mut self) -> MaybeError;
|
||||||
|
fn reload(&mut self) -> MaybeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BatchedRepository<T, D> where D : BatchedDbLayer<T>, T: EntityData
|
||||||
|
{
|
||||||
|
fn save_many(&mut self, handles: &mut [EntityHandle<T>]) -> MaybeError;
|
||||||
|
fn delete_many(&mut self, handles: &mut [EntityHandle<T>]) -> MaybeError;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DbLayer<T> {
|
||||||
|
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<T>, connection: &Connection) -> MaybeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BatchedDbLayer<T> 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)]
|
#[derive(Clone)]
|
||||||
pub struct WordRepository {
|
pub struct DataRepository<T, D> {
|
||||||
inner: Arc<RwLock<WordRepositoryInner>>,
|
inner: Arc<RwLock<DataRepositoryInner<T, D>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WordRepositoryInner {
|
pub struct DataRepositoryInner<T, D> {
|
||||||
cache: Vec<WordData>,
|
cache: Vec<T>,
|
||||||
temporal: Vec<Entity<WordData, WordRepository>>,
|
temporal: Vec<T>,
|
||||||
next_temporal: u32,
|
next_temporal: u32,
|
||||||
connection: Arc<Mutex<Connection>>,
|
connection: Arc<Mutex<Connection>>,
|
||||||
|
data_layer: D,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repository<WordData> for WordRepository {
|
impl<T, D> Deref for DataRepository<T, D> {
|
||||||
fn get(&self, id: Id) -> Option<WordData> {
|
type Target = Arc<RwLock<DataRepositoryInner<T, D>>>;
|
||||||
let inner = self.inner.read();
|
|
||||||
Some(inner.cache[inner.cache.binary_search_by(|w| w.id.cmp(&id)).ok()?].clone())
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: EntityData + Clone, D: DbLayer<T>> Repository<T, D> for DataRepositoryInner<T, D> {
|
||||||
|
fn get(&self, handle: &mut EntityHandle<T>) -> 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].clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(&mut self, new: WordData) -> Entity<WordData, WordRepository> {
|
fn get_ref(&self, handle: &mut EntityHandle<T>) -> Option<&T> {
|
||||||
let mut inner = self.inner.write();
|
let slice = if handle.real {
|
||||||
let next_temporal = inner.next_temporal;
|
self.cache.as_slice()
|
||||||
inner.next_temporal += 1;
|
} else {
|
||||||
drop(inner);
|
self.temporal.as_slice()
|
||||||
let entity = Entity {
|
};
|
||||||
inner: EntityInner {
|
let index = fast_position_of(slice, handle)?;
|
||||||
|
Some(&slice[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mut(&mut self, handle: &mut EntityHandle<T>) -> 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<T> {
|
||||||
|
EntityHandle{
|
||||||
|
_phantom: Default::default(),
|
||||||
|
id,
|
||||||
|
real: true,
|
||||||
|
index: id.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn get_static(&self, mut handle: EntityHandle<T>) -> 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].clone())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn get_ref_static(&self, mut handle: EntityHandle<T>) -> 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<T>) -> 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<T> {
|
||||||
|
let next_temporal = self.next_temporal;
|
||||||
|
self.next_temporal += 1;
|
||||||
|
let entity = EntityHandle {
|
||||||
id: next_temporal.into(),
|
id: next_temporal.into(),
|
||||||
real: false,
|
real: false,
|
||||||
},
|
index: self.temporal.len() as u32,
|
||||||
repo: self.clone(),
|
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
};
|
};
|
||||||
|
new.set_id(entity.id);
|
||||||
|
self.temporal.push(new);
|
||||||
entity
|
entity
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&mut self, entity: EntityInner) -> MaybeError {
|
fn save(&mut self, entity: &mut EntityHandle<T>) -> MaybeError {
|
||||||
todo!()
|
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<T>) -> 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<T: EntityData + Clone, D: BatchedDbLayer<T>> BatchedRepository<T, D> for DataRepositoryInner<T, D> {
|
||||||
|
fn save_many(&mut self, handles: &mut [EntityHandle<T>]) -> MaybeError {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_many(&mut self, handles: &mut [EntityHandle<T>]) -> 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<E: EntityData>(
|
||||||
|
storage: &[E],
|
||||||
|
handle: &mut EntityHandle<E>,
|
||||||
|
) -> Option<usize> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ mod navigation;
|
|||||||
pub mod notification;
|
pub mod notification;
|
||||||
mod pages;
|
mod pages;
|
||||||
mod styling;
|
mod styling;
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
use crate::app::start;
|
use crate::app::start;
|
||||||
use mimalloc::MiMalloc;
|
use mimalloc::MiMalloc;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user