dry some stuff. also lot of yelling at the compiler

master
Ondřej Hruška 4 years ago
parent e810724cd1
commit 3997381748
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 1
      Cargo.lock
  2. 24
      yopa-web/src/routes/models/object.rs
  3. 70
      yopa-web/src/routes/objects.rs
  4. 1
      yopa/Cargo.toml
  5. 8
      yopa/src/cool.rs
  6. 2
      yopa/src/data.rs
  7. 200
      yopa/src/helpers.rs
  8. 13
      yopa/src/id.rs
  9. 6
      yopa/src/insert.rs
  10. 80
      yopa/src/lib.rs
  11. 9
      yopa/src/serde_atomic_id.rs
  12. 1
      yopa/src/tests.rs

1
Cargo.lock generated

@ -2714,6 +2714,7 @@ dependencies = [
"itertools",
"log",
"parking_lot",
"rand 0.8.3",
"serde",
"serde_json",
"thiserror",

@ -41,7 +41,7 @@ pub(crate) struct ObjectModelForm {
pub name: String,
#[serde(default)]
// #[serde(with="serde_with::rust::default_on_error")] // This is because "" can be selected
#[serde(with="my_string_empty_as_none")]
#[serde(with = "my_string_empty_as_none")]
pub name_property: Option<ID>,
}
@ -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

@ -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) => {

@ -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 = []

@ -40,18 +40,16 @@ impl<K, V> KVVecToKeysOrValues<K, V> for Vec<(K, V)> {
}
}
pub(crate) trait IsNoneOrElse<T> : Sized {
pub(crate) trait IsNoneOrElse<T>: Sized {
//noinspection RsSelfConvention
fn is_none_or_else(&self, test : impl FnOnce(&T) -> bool) -> bool;
fn is_none_or_else(&self, test: impl FnOnce(&T) -> bool) -> bool;
}
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,6 +1,6 @@
//! Data value structs
use std::borrow::{Cow, Borrow};
use std::borrow::{Borrow, Cow};
use serde::{Deserialize, Serialize};

@ -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();
// }
}

@ -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()
}

@ -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,

@ -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)]
@ -40,8 +44,8 @@ mod tests;
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub const YOPA_MAGIC : &[u8; 4] = b"YOPA";
pub const BINARY_FORMAT : u8 = 1;
pub const YOPA_MAGIC: &[u8; 4] = b"YOPA";
pub const BINARY_FORMAT: u8 = 1;
/// Stupid storage with naive inefficient file persistence
#[derive(Debug, Default, Serialize, Deserialize)]
@ -62,7 +66,7 @@ pub struct Storage {
#[cfg(not(feature = "uuid-ids"))]
#[serde(with = "serde_atomic_id")]
next_id : atomic::Atomic<ID>,
next_id: atomic::Atomic<ID>,
#[serde(skip)]
opts: StoreOpts,
@ -180,7 +184,7 @@ impl Storage {
let parsed: Self = match self.opts.file_format {
FileEncoding::JSON => serde_json::from_reader(reader)?,
FileEncoding::BINCODE => {
let mut magic : [u8; 5] = [0; 5];
let mut magic: [u8; 5] = [0; 5];
reader.read_exact(&mut magic)?;
if &magic[0..4] != YOPA_MAGIC {
@ -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)?
},
}
};
}
}
@ -253,7 +257,7 @@ impl Storage {
}
/// Get object name, or the ID as string if no name is configured
pub fn get_object_name_by_id<'a>(&'a self, id : ID) -> Cow<'a, str> {
pub fn get_object_name_by_id<'a>(&'a self, id: ID) -> Cow<'a, str> {
if let Some(o) = self.get_object(id) {
return self.get_object_name(o);
}
@ -261,9 +265,17 @@ impl Storage {
return id.to_string().into();
}
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) {
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)
{
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);

@ -1,20 +1,19 @@
//! 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)?;
let id: ID = ID::deserialize(deserializer)?;
Ok(atomic::Atomic::new(id))
}

@ -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());

Loading…
Cancel
Save