a small relational database with user-editable schema for manual data entry
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
yopa/yopa/src/helpers.rs

200 lines
5.2 KiB

//! Helper traits and stuff for working with lists and iterators of objects
// Re-export itertools for convenience
pub use itertools;
use crate::{data, insert, model, update, ID};
use itertools::Itertools;
use std::collections::HashMap;
pub trait GetParent {
fn get_parent(&self) -> ID;
}
macro_rules! impl_get_parent {
($($struct:ty),+) => {
$(
impl GetParent for $struct {
fn get_parent(&self) -> ID { self.object }
}
impl GetParent for &$struct {
fn get_parent(&self) -> ID { self.object }
}
impl GetParent for &&$struct {
fn get_parent(&self) -> ID { self.object }
}
)+
}
}
impl_get_parent!(
data::Value, data::Relation,
model::RelationModel, model::PropertyModel);
pub trait GetModelID {
fn get_model_id(&self) -> ID;
}
macro_rules! impl_get_model_id {
($($struct:ty),+) => {
$(
impl GetModelID for $struct {
fn get_model_id(&self) -> ID { self.model }
}
impl GetModelID for &$struct {
fn get_model_id(&self) -> ID { self.model }
}
impl GetModelID for &&$struct {
fn get_model_id(&self) -> ID { self.model }
}
)+
}
}
impl_get_model_id!(
data::Object, data::Value, data::Relation,
insert::InsertRel, insert::InsertValue, insert::InsertObj,
update::UpsertRelation, update::UpsertValue);
// This would be nice, but doesn't work
//
// pub struct FilterOfParent<'a, S: 'a> { iter: S, parent: ID, marker : std::marker::PhantomData<&'a S> }
//
// // impl<'a, V: GetParent + 'a, S: Iterator<Item=&'a V>> Iterator for FilterOfParent<S> {
// // type Item = &'a V;
// //
// // #[inline]
// // fn next(&mut self) -> Option<Self::Item> {
// // let parent = self.parent;
// // self.iter.find(|x| x.get_parent() == parent)
// // }
// // }
//
// impl<'a, V: GetParent + 'a, S: Iterator<Item=V>> Iterator for FilterOfParent<'a, S> {
// type Item = V;
//
// #[inline]
// fn next(&mut self) -> Option<Self::Item> {
// let parent = self.parent;
// self.iter.find(|x| x.get_parent() == parent)
// }
// }
//
// pub trait WhereParent<'a>: Sized {
// fn where_parent(self, id: ID) -> FilterOfParent<'a, Self>;
// }
//
// impl<'a, V : 'a, S : 'a + Iterator<Item=V>> WhereParent<'a> for S {
// fn where_parent(self, id: ID) -> FilterOfParent<'a, Self> {
// FilterOfParent { iter: self, parent: id, marker: std::marker::PhantomData }
// }
// }
pub trait GroupByParent<'a, T: 'a + GetParent>: Sized {
fn group_by_parent(self) -> HashMap<ID, Vec<T>>;
}
impl<'a, T: 'a + GetParent, S: IntoIterator<Item = T>> GroupByParent<'a, T> for S {
#[inline]
fn group_by_parent(self) -> HashMap<ID, Vec<T>> {
self.into_iter().into_group_map_by(|v| v.get_parent())
}
}
pub trait GroupByModel<'a, T: 'a + GetModelID>: Sized {
fn group_by_model(self) -> HashMap<ID, Vec<T>>;
}
impl<'a, T: 'a + GetModelID, S: IntoIterator<Item = T>> GroupByModel<'a, T> for S {
#[inline]
fn group_by_model(self) -> HashMap<ID, Vec<T>> {
self.into_iter().into_group_map_by(|v| v.get_model_id())
}
}
#[cfg(test)]
mod tests {
use crate::helpers::{GetParent, GroupByParent};
use crate::id::random_id;
use crate::ID;
#[derive(Debug, PartialEq, Eq)]
struct Child(ID, &'static str);
impl GetParent for Child {
fn get_parent(&self) -> ID {
self.0
}
}
#[test]
fn group_vec() {
let parent1 = random_id();
let parent2 = random_id();
let vec = vec![
Child(parent1, "A"),
Child(parent2, "C"),
Child(parent1, "B"),
Child(parent2, "D"),
];
let mut grouped = vec.group_by_parent();
assert_eq!(2, grouped.len());
assert_eq!(
grouped.remove(&parent1).unwrap(),
vec![Child(parent1, "A"), Child(parent1, "B"),]
);
assert_eq!(
grouped.remove(&parent2).unwrap(),
vec![Child(parent2, "C"), Child(parent2, "D"),]
);
}
#[test]
fn group_iter() {
let parent1 = random_id();
let parent2 = random_id();
let vec = vec![
Child(parent1, "A"),
Child(parent2, "C"),
Child(parent1, "B"),
Child(parent2, "D"),
];
let mut grouped = vec.into_iter().group_by_parent();
assert_eq!(2, grouped.len());
assert_eq!(
grouped.remove(&parent1).unwrap(),
vec![Child(parent1, "A"), Child(parent1, "B"),]
);
assert_eq!(
grouped.remove(&parent2).unwrap(),
vec![Child(parent2, "C"), Child(parent2, "D"),]
);
}
// #[test]
// fn count_where_parent() {
// let parent1 = random_id();
// let parent2 = random_id();
//
// let vec = vec![
// Child(parent1, "A"),
// Child(parent2, "C"),
// Child(parent1, "B"),
// Child(parent2, "D"),
// ];
//
// let _ = vec.iter().where_parent(parent2).next();
// }
}