dry some stuff. also lot of yelling at the compiler

This commit is contained in:
2021-02-23 00:14:09 +01:00
parent e810724cd1
commit 3997381748
12 changed files with 350 additions and 127 deletions
Generated
+1
View File
@@ -2714,6 +2714,7 @@ dependencies = [
"itertools",
"log",
"parking_lot",
"rand 0.8.3",
"serde",
"serde_json",
"thiserror",
+12 -10
View File
@@ -56,7 +56,7 @@ pub(crate) async fn create(
match wg.define_object(ObjectModel {
id: Default::default(),
name: form.name.clone(),
name_property: form.name_property
name_property: form.name_property,
}) {
Ok(_id) => {
wg.persist().err_to_500()?;
@@ -96,10 +96,13 @@ pub(crate) async fn update_form(
context.insert("model", &model);
} else {
context.insert("model", model);
context.insert("old", &ObjectModelForm {
context.insert(
"old",
&ObjectModelForm {
name: model.name.to_string(),
name_property: model.name_property
});
name_property: model.name_property,
},
);
}
let properties = rg.get_property_models_for_parent(*model_id).collect_vec();
@@ -162,14 +165,13 @@ pub(crate) async fn delete(
}
}
pub mod my_string_empty_as_none {
use serde::{Deserializer, Serializer, Serialize};
use std::str::FromStr;
use std::fmt::{Display, Write};
use std::marker::PhantomData;
use serde::de::{Visitor, Error};
use serde::de::{Error, Visitor};
use serde::{Deserializer, Serialize, Serializer};
use std::fmt;
use std::fmt::Display;
use std::marker::PhantomData;
use std::str::FromStr;
use yopa::ID;
// FIXME largely copied from serde_with
+41 -29
View File
@@ -1,23 +1,24 @@
use std::borrow::{Borrow, Cow};
use std::borrow::Cow;
use std::collections::HashMap;
use actix_session::Session;
use actix_web::{HttpResponse, Responder, web};
use actix_web::{web, HttpResponse, Responder};
use heck::TitleCase;
use itertools::Itertools;
use json_dotpath::DotPaths;
use serde::Serialize;
use yopa::{data, ID, model, Storage};
use yopa::data::Object;
use yopa::{data, model, Storage, ID};
use yopa::insert::InsertObj;
use yopa::model::{ObjectModel, PropertyModel, RelationModel};
use yopa::update::UpdateObj;
use crate::session_ext::SessionExt;
use crate::TERA;
use crate::tera_ext::TeraExt;
use crate::utils::{redirect, StorageErrorIntoResponseError};
use crate::TERA;
use yopa::helpers::GroupByModel;
// we only need references here, Context serializes everything to Value.
// cloning would be a waste of cycles
@@ -97,13 +98,14 @@ fn prepare_object_create_data(rg: &Storage, model_id: ID) -> actix_web::Result<O
.get_property_models_for_parents(prop_object_ids)
.collect(),
},
objects: rg.get_objects_of_types(related_ids).map(|o| {
ObjectDisplay {
objects: rg
.get_objects_of_types(related_ids)
.map(|o| ObjectDisplay {
id: o.id,
model: o.model,
name: rg.get_object_name(o),
}
}).collect(),
})
.collect(),
})
}
@@ -162,13 +164,16 @@ pub(crate) async fn list_inner(
.sorted_by_key(|m| &m.name)
.map(|model| {
let objects = objects_by_model.remove(&model.id).unwrap_or_default();
let mut objects = objects.into_iter().map(|o| {
let mut objects = objects
.into_iter()
.map(|o| {
ObjectDisplay {
id: o.id,
model: o.model,
name: rg.get_object_name(o), // TODO optimize
}
}).collect_vec();
})
.collect_vec();
objects.sort_by(|a, b| a.name.cmp(&b.name));
ModelWithObjects { model, objects }
@@ -223,11 +228,14 @@ pub(crate) async fn detail(
.get_object_model(object.model)
.ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?;
context.insert("object", &ObjectDisplay {
context.insert(
"object",
&ObjectDisplay {
id: object_id,
model: object.model,
name: rg.get_object_name(object),
});
},
);
context.insert("model", model);
context.insert("kind", &rg.get_model_name(object.model));
@@ -248,8 +256,7 @@ pub(crate) async fn detail(
let object_values_by_model = grouped_values
.remove(&object_id)
.unwrap_or_default()
.into_iter()
.into_group_map_by(|value| value.model);
.group_by_model();
let mut view_object_properties = vec![];
for (prop_model_id, values) in object_values_by_model {
@@ -268,7 +275,7 @@ pub(crate) async fn detail(
{
let grouped_relations = relations
.iter()
.into_group_map_by(|relation| relation.model);
.group_by_model();
let mut relation_views = vec![];
for (model_id, relations) in grouped_relations {
@@ -283,8 +290,7 @@ pub(crate) async fn detail(
let rel_values_by_model = grouped_values
.remove(&rel.id)
.unwrap_or_default()
.into_iter()
.into_group_map_by(|value| value.model);
.group_by_model();
let mut view_rel_properties = vec![];
for (prop_model_id, values) in rel_values_by_model {
@@ -327,7 +333,7 @@ pub(crate) async fn detail(
{
let grouped_relations = reci_relations
.iter()
.into_group_map_by(|relation| relation.model);
.group_by_model();
let mut relation_views = vec![];
for (model_id, relations) in grouped_relations {
@@ -342,8 +348,7 @@ pub(crate) async fn detail(
let rel_values_by_model = grouped_values
.remove(&rel.id)
.unwrap_or_default()
.into_iter()
.into_group_map_by(|value| value.model);
.group_by_model();
let mut view_rel_properties = vec![];
for (prop_model_id, values) in rel_values_by_model {
@@ -428,11 +433,14 @@ pub(crate) async fn update_form(
// maybe its useful,idk
context.insert("model", &model);
context.insert("object", &ObjectDisplay {
context.insert(
"object",
&ObjectDisplay {
id: object.id,
model: object.model,
name: rg.get_object_name(object),
});
},
);
let create_data = prepare_object_create_data(&rg, model.id)?;
@@ -448,7 +456,8 @@ pub(crate) async fn update_form(
.map(|p| p.id)
.collect_vec();
let mut values_grouped = rg.get_values_for_object(*id).into_group_map_by(|v| v.model);
let mut values_grouped = rg.get_values_for_object(*id)
.group_by_model();
prop_ids.into_iter().for_each(|id| {
value_map.insert(
@@ -472,7 +481,7 @@ pub(crate) async fn update_form(
let mut relations_grouped_by_model = relations
.iter()
.into_group_map_by(|relation| relation.model);
.group_by_model();
let mut property_models_grouped_by_parent =
rg.get_grouped_prop_models_for_parents(relation_model_ids.clone());
@@ -497,8 +506,7 @@ pub(crate) async fn update_form(
let mut rel_values = relation_values_grouped_by_instance
.remove(&rel.id)
.unwrap_or_default()
.into_iter()
.into_group_map_by(|relation| relation.model);
.group_by_model();
prop_models_for_relation.iter().for_each(|prop_model| {
relation_values_map.insert(
@@ -525,7 +533,7 @@ pub(crate) async fn update_form(
let object = EnrichedObject {
id: object.id,
model: object.model,
name: rg.get_object_name_by_id(object.id),
name: rg.get_object_name(object),
values: value_map,
relations: relation_map,
};
@@ -558,7 +566,11 @@ pub(crate) async fn update(
Ok(_id) => {
wg.persist().err_to_500()?;
debug!("Object created, redirecting to root");
session.flash_success(format!("{} \"{}\" updated.", model_name, wg.get_object_name_by_id(id)));
session.flash_success(format!(
"{} \"{}\" updated.",
model_name,
wg.get_object_name_by_id(id)
));
Ok(HttpResponse::Ok().finish())
}
Err(e) => {
+1
View File
@@ -22,6 +22,7 @@ itertools = "0.10.0"
#lazy_static = "1.4.0"
bincode = "1.3.1"
atomic = "0.5.0"
rand = "0.8.3"
[features]
default = []
+1 -3
View File
@@ -49,9 +49,7 @@ impl<T> IsNoneOrElse<T> for Option<T> {
fn is_none_or_else(&self, test: impl FnOnce(&T) -> bool) -> bool {
match self {
None => true,
Some(value) => {
test(value)
}
Some(value) => test(value),
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
//! Data value structs
use std::borrow::{Cow, Borrow};
use std::borrow::{Borrow, Cow};
use serde::{Deserialize, Serialize};
+200
View File
@@ -0,0 +1,200 @@
//! 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();
// }
}
+13
View File
@@ -17,3 +17,16 @@ pub type ID = u32;
pub trait HaveId {
fn get_id(&self) -> ID;
}
#[cfg(feature = "uuid-ids")]
#[cfg(test)]
pub fn random_id() -> ID {
ID::new_v4()
}
#[cfg(not(feature = "uuid-ids"))]
#[cfg(test)]
pub fn random_id() -> ID {
use rand::Rng;
rand::thread_rng().gen()
}
+1 -5
View File
@@ -48,11 +48,7 @@ pub struct InsertObj {
}
impl InsertObj {
pub fn new(
model_id: ID,
values: Vec<InsertValue>,
relations: Vec<InsertRel>,
) -> Self {
pub fn new(model_id: ID, values: Vec<InsertValue>, relations: Vec<InsertRel>) -> Self {
Self {
model: model_id,
values,
+34 -34
View File
@@ -1,7 +1,8 @@
#[macro_use]
extern crate log;
// #[macro_use]
// extern crate serde_json;
#[cfg(test)]
#[macro_use]
extern crate serde_json;
use std::borrow::Cow;
use std::collections::HashMap;
@@ -21,10 +22,11 @@ use insert::InsertValue;
pub use model::DataType;
use model::ObjectModel;
use crate::cool::IsNoneOrElse;
use crate::data::Object;
use crate::helpers::{GroupByModel, GroupByParent};
use crate::model::{PropertyModel, RelationModel};
use crate::update::{UpdateObj, UpsertValue};
use crate::cool::IsNoneOrElse;
use crate::data::{Object, Value};
mod cool;
pub mod data;
@@ -33,6 +35,8 @@ pub mod insert;
pub mod model;
pub mod update;
pub mod helpers;
mod serde_atomic_id;
mod serde_map_as_list;
#[cfg(test)]
@@ -193,7 +197,7 @@ impl Storage {
}
bincode::deserialize_from(reader)?
},
}
};
let opts = std::mem::replace(&mut self.opts, StoreOpts::default());
@@ -231,7 +235,7 @@ impl Storage {
writer.write_all(YOPA_MAGIC)?;
writer.write_all(&[BINARY_FORMAT])?;
bincode::serialize_into(writer, self)?
},
}
};
}
}
@@ -262,8 +266,16 @@ impl Storage {
}
pub fn get_object_name<'b, 'a: 'b>(&'a self, object: &'a Object) -> Cow<'b, str> {
if let Some(ObjectModel{ name_property: Some(name_property), .. }) = self.get_object_model(object.model) {
if let Some(v) = self.values.values().find(|v| v.object == object.id && &v.model == name_property) {
if let Some(ObjectModel {
name_property: Some(name_property),
..
}) = self.get_object_model(object.model)
{
if let Some(v) = self
.values
.values()
.find(|v| v.object == object.id && &v.model == name_property)
{
return v.value.to_cow();
}
}
@@ -293,7 +305,9 @@ impl Storage {
/// Iterate object models with given IDs
pub fn get_object_models_by_ids(&self, ids: Vec<ID>) -> impl Iterator<Item = &ObjectModel> {
self.obj_models.values().filter(move |m| ids.contains(&m.id))
self.obj_models
.values()
.filter(move |m| ids.contains(&m.id))
}
/// Get an object model by ID
@@ -313,9 +327,7 @@ impl Storage {
/// Get all property models grouped by the parent object ID.
pub fn get_grouped_prop_models(&self) -> HashMap<ID, Vec<&PropertyModel>> {
self.prop_models
.values()
.into_group_map_by(|model| model.object)
self.prop_models.values().group_by_parent()
}
/// Get property models belonging to a group of parent IDs,
@@ -327,7 +339,7 @@ impl Storage {
self.prop_models
.values()
.filter(|p| parent_model_ids.contains(&p.object))
.into_group_map_by(|model| model.object)
.group_by_parent()
}
/// Iterate relation models attached to an object model
@@ -362,9 +374,7 @@ impl Storage {
/// Get all relation models, grouped by their source object model ID
pub fn get_grouped_relation_models(&self) -> HashMap<ID, Vec<&RelationModel>> {
self.rel_models
.values()
.into_group_map_by(|model| model.object)
self.rel_models.values().group_by_parent()
}
/// Get reciprocal relation models, grouped by their destination model ID
@@ -411,7 +421,7 @@ impl Storage {
self.values
.values()
.filter(move |prop| parents.contains(&prop.object))
.into_group_map_by(|model| model.object)
.group_by_parent()
}
/// Get all objects belonging to a model.
@@ -432,9 +442,7 @@ impl Storage {
/// Get all objects, grouped by their model ID
pub fn get_grouped_objects(&self) -> HashMap<ID, Vec<&data::Object>> {
self.objects
.values()
.into_group_map_by(|object| object.model)
self.objects.values().group_by_model()
}
/// Get object by ID
@@ -830,7 +838,7 @@ impl Storage {
parent_id: ID,
parent_model_id: ID|
-> Result<Vec<data::Value>, StorageError> {
let mut values_by_id = values.into_iter().into_group_map_by(|iv| iv.model);
let mut values_by_id = values.group_by_model();
let mut values_to_insert = vec![];
for (prop_model_id, prop) in self
@@ -908,10 +916,7 @@ impl Storage {
let mut values_to_insert = find_values_to_insert(insobj.values, object_id, obj_model_id)?;
// And now ..... relations!
let mut relations_by_id = insobj
.relations
.into_iter()
.into_group_map_by(|ir| ir.model);
let mut relations_by_id = insobj.relations.group_by_model();
let mut relations_to_insert = vec![];
for (relation_model_id, relation_model) in self
@@ -1020,7 +1025,7 @@ impl Storage {
),
StorageError,
> {
let mut values_by_model = values.into_iter().into_group_map_by(|iv| iv.model);
let mut values_by_model = values.group_by_model();
let mut values_to_insert = vec![];
let mut ids_to_delete = vec![];
@@ -1028,7 +1033,7 @@ impl Storage {
.values
.values()
.filter(|v| v.object == parent_id)
.into_group_map_by(|v| v.model);
.group_by_model();
for (prop_model_id, prop) in self
.prop_models
@@ -1113,10 +1118,7 @@ impl Storage {
find_values_to_change(updobj.values, updated_object_id, updated_object_model_id)?;
// And now ..... relations!
let mut relations_by_model = updobj
.relations
.into_iter()
.into_group_map_by(|ir| ir.model);
let mut relations_by_model = updobj.relations.group_by_model();
let mut relations_to_insert = vec![];
let mut relations_to_delete = vec![];
@@ -1124,7 +1126,7 @@ impl Storage {
.relations
.values()
.filter(|v| v.object == updated_object_id)
.into_group_map_by(|v| v.model);
.group_by_model();
let rel_models_by_id = self
.rel_models
@@ -1196,8 +1198,6 @@ impl Storage {
);
}
let obj_mut = self.objects.get_mut(&updated_object_id).unwrap();
debug!("Add {} new object relations", relations_to_insert.len());
for rel in relations_to_insert {
self.relations.insert(rel.id, rel);
+3 -4
View File
@@ -1,19 +1,18 @@
//! Serialize atomic ID by fetching its value
use crate::id::ID;
use serde::{Serialize, Deserializer, Deserialize};
use serde::{Deserialize, Deserializer, Serialize};
pub fn serialize<S>(x: &atomic::Atomic<ID>, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
x.load(atomic::Ordering::Relaxed)
.serialize(s)
x.load(atomic::Ordering::Relaxed).serialize(s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<atomic::Atomic<ID>, D::Error>
where
D: Deserializer<'de>
D: Deserializer<'de>,
{
let id: ID = ID::deserialize(deserializer)?;
Ok(atomic::Atomic::new(id))
+1
View File
@@ -5,6 +5,7 @@ fn test1() {
let model = crate::model::ObjectModel {
id: Default::default(),
name: "Name".to_string(),
name_property: None,
};
println!("{}", serde_json::to_string_pretty(&model).unwrap());