dry some stuff. also lot of yelling at the compiler

master
Ondřej Hruška 3 years ago
parent e810724cd1
commit 3997381748
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 1
      Cargo.lock
  2. 60
      yopa-web/src/routes/models/object.rs
  3. 92
      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. 84
      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", "itertools",
"log", "log",
"parking_lot", "parking_lot",
"rand 0.8.3",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",

@ -41,7 +41,7 @@ pub(crate) struct ObjectModelForm {
pub name: String, pub name: String,
#[serde(default)] #[serde(default)]
// #[serde(with="serde_with::rust::default_on_error")] // This is because "" can be selected // #[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>, pub name_property: Option<ID>,
} }
@ -56,7 +56,7 @@ pub(crate) async fn create(
match wg.define_object(ObjectModel { match wg.define_object(ObjectModel {
id: Default::default(), id: Default::default(),
name: form.name.clone(), name: form.name.clone(),
name_property: form.name_property name_property: form.name_property,
}) { }) {
Ok(_id) => { Ok(_id) => {
wg.persist().err_to_500()?; wg.persist().err_to_500()?;
@ -96,10 +96,13 @@ pub(crate) async fn update_form(
context.insert("model", &model); context.insert("model", &model);
} else { } else {
context.insert("model", model); context.insert("model", model);
context.insert("old", &ObjectModelForm { context.insert(
name: model.name.to_string(), "old",
name_property: model.name_property &ObjectModelForm {
}); name: model.name.to_string(),
name_property: model.name_property,
},
);
} }
let properties = rg.get_property_models_for_parent(*model_id).collect_vec(); let properties = rg.get_property_models_for_parent(*model_id).collect_vec();
@ -162,30 +165,29 @@ pub(crate) async fn delete(
} }
} }
pub mod my_string_empty_as_none { pub mod my_string_empty_as_none {
use serde::{Deserializer, Serializer, Serialize}; use serde::de::{Error, Visitor};
use std::str::FromStr; use serde::{Deserializer, Serialize, Serializer};
use std::fmt::{Display, Write};
use std::marker::PhantomData;
use serde::de::{Visitor, Error};
use std::fmt; use std::fmt;
use std::fmt::Display;
use std::marker::PhantomData;
use std::str::FromStr;
use yopa::ID; use yopa::ID;
// FIXME largely copied from serde_with // FIXME largely copied from serde_with
/// Deserialize an `Option<T>` from a string using `FromStr` /// Deserialize an `Option<T>` from a string using `FromStr`
pub fn deserialize<'de, D, S>(deserializer: D) -> Result<Option<S>, D::Error> pub fn deserialize<'de, D, S>(deserializer: D) -> Result<Option<S>, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
S: FromStr, S: FromStr,
S::Err: Display, S::Err: Display,
{ {
struct OptionStringEmptyNone<S>(PhantomData<S>); struct OptionStringEmptyNone<S>(PhantomData<S>);
impl<'de, S> Visitor<'de> for OptionStringEmptyNone<S> impl<'de, S> Visitor<'de> for OptionStringEmptyNone<S>
where where
S: FromStr, S: FromStr,
S::Err: Display, S::Err: Display,
{ {
type Value = Option<S>; type Value = Option<S>;
@ -194,8 +196,8 @@ pub mod my_string_empty_as_none {
} }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where where
E: Error, E: Error,
{ {
match value { match value {
"" => Ok(None), "" => Ok(None),
@ -204,8 +206,8 @@ pub mod my_string_empty_as_none {
} }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where where
E: Error, E: Error,
{ {
match &*value { match &*value {
"" => Ok(None), "" => Ok(None),
@ -215,16 +217,16 @@ pub mod my_string_empty_as_none {
// TODO remove? // TODO remove?
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where where
E: Error, E: Error,
{ {
self.visit_str(&v.to_string()) self.visit_str(&v.to_string())
} }
// handles the `null` case // handles the `null` case
fn visit_unit<E>(self) -> Result<Self::Value, E> fn visit_unit<E>(self) -> Result<Self::Value, E>
where where
E: Error, E: Error,
{ {
Ok(None) Ok(None)
} }
@ -235,8 +237,8 @@ pub mod my_string_empty_as_none {
/// Serialize a string from `Option<T>` using `AsRef<str>` or using the empty string if `None`. /// Serialize a string from `Option<T>` using `AsRef<str>` or using the empty string if `None`.
pub fn serialize<S>(option: &Option<ID>, serializer: S) -> Result<S::Ok, S::Error> pub fn serialize<S>(option: &Option<ID>, serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
{ {
option.serialize(serializer) option.serialize(serializer)

@ -1,23 +1,24 @@
use std::borrow::{Borrow, Cow}; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use actix_session::Session; use actix_session::Session;
use actix_web::{HttpResponse, Responder, web}; use actix_web::{web, HttpResponse, Responder};
use heck::TitleCase; use heck::TitleCase;
use itertools::Itertools; use itertools::Itertools;
use json_dotpath::DotPaths; use json_dotpath::DotPaths;
use serde::Serialize; use serde::Serialize;
use yopa::{data, ID, model, Storage}; use yopa::{data, model, Storage, ID};
use yopa::data::Object;
use yopa::insert::InsertObj; use yopa::insert::InsertObj;
use yopa::model::{ObjectModel, PropertyModel, RelationModel}; use yopa::model::{ObjectModel, PropertyModel, RelationModel};
use yopa::update::UpdateObj; use yopa::update::UpdateObj;
use crate::session_ext::SessionExt; use crate::session_ext::SessionExt;
use crate::TERA;
use crate::tera_ext::TeraExt; use crate::tera_ext::TeraExt;
use crate::utils::{redirect, StorageErrorIntoResponseError}; use crate::utils::{redirect, StorageErrorIntoResponseError};
use crate::TERA;
use yopa::helpers::GroupByModel;
// we only need references here, Context serializes everything to Value. // we only need references here, Context serializes everything to Value.
// cloning would be a waste of cycles // 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) .get_property_models_for_parents(prop_object_ids)
.collect(), .collect(),
}, },
objects: rg.get_objects_of_types(related_ids).map(|o| { objects: rg
ObjectDisplay { .get_objects_of_types(related_ids)
.map(|o| ObjectDisplay {
id: o.id, id: o.id,
model: o.model, model: o.model,
name: rg.get_object_name(o), name: rg.get_object_name(o),
} })
}).collect(), .collect(),
}) })
} }
@ -162,13 +164,16 @@ pub(crate) async fn list_inner(
.sorted_by_key(|m| &m.name) .sorted_by_key(|m| &m.name)
.map(|model| { .map(|model| {
let objects = objects_by_model.remove(&model.id).unwrap_or_default(); let objects = objects_by_model.remove(&model.id).unwrap_or_default();
let mut objects = objects.into_iter().map(|o| { let mut objects = objects
ObjectDisplay { .into_iter()
id: o.id, .map(|o| {
model: o.model, ObjectDisplay {
name: rg.get_object_name(o), // TODO optimize id: o.id,
} model: o.model,
}).collect_vec(); name: rg.get_object_name(o), // TODO optimize
}
})
.collect_vec();
objects.sort_by(|a, b| a.name.cmp(&b.name)); objects.sort_by(|a, b| a.name.cmp(&b.name));
ModelWithObjects { model, objects } ModelWithObjects { model, objects }
@ -223,11 +228,14 @@ pub(crate) async fn detail(
.get_object_model(object.model) .get_object_model(object.model)
.ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?; .ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?;
context.insert("object", &ObjectDisplay { context.insert(
id: object_id, "object",
model: object.model, &ObjectDisplay {
name: rg.get_object_name(object), id: object_id,
}); model: object.model,
name: rg.get_object_name(object),
},
);
context.insert("model", model); context.insert("model", model);
context.insert("kind", &rg.get_model_name(object.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 let object_values_by_model = grouped_values
.remove(&object_id) .remove(&object_id)
.unwrap_or_default() .unwrap_or_default()
.into_iter() .group_by_model();
.into_group_map_by(|value| value.model);
let mut view_object_properties = vec![]; let mut view_object_properties = vec![];
for (prop_model_id, values) in object_values_by_model { for (prop_model_id, values) in object_values_by_model {
@ -268,7 +275,7 @@ pub(crate) async fn detail(
{ {
let grouped_relations = relations let grouped_relations = relations
.iter() .iter()
.into_group_map_by(|relation| relation.model); .group_by_model();
let mut relation_views = vec![]; let mut relation_views = vec![];
for (model_id, relations) in grouped_relations { for (model_id, relations) in grouped_relations {
@ -283,8 +290,7 @@ pub(crate) async fn detail(
let rel_values_by_model = grouped_values let rel_values_by_model = grouped_values
.remove(&rel.id) .remove(&rel.id)
.unwrap_or_default() .unwrap_or_default()
.into_iter() .group_by_model();
.into_group_map_by(|value| value.model);
let mut view_rel_properties = vec![]; let mut view_rel_properties = vec![];
for (prop_model_id, values) in rel_values_by_model { for (prop_model_id, values) in rel_values_by_model {
@ -327,7 +333,7 @@ pub(crate) async fn detail(
{ {
let grouped_relations = reci_relations let grouped_relations = reci_relations
.iter() .iter()
.into_group_map_by(|relation| relation.model); .group_by_model();
let mut relation_views = vec![]; let mut relation_views = vec![];
for (model_id, relations) in grouped_relations { for (model_id, relations) in grouped_relations {
@ -342,8 +348,7 @@ pub(crate) async fn detail(
let rel_values_by_model = grouped_values let rel_values_by_model = grouped_values
.remove(&rel.id) .remove(&rel.id)
.unwrap_or_default() .unwrap_or_default()
.into_iter() .group_by_model();
.into_group_map_by(|value| value.model);
let mut view_rel_properties = vec![]; let mut view_rel_properties = vec![];
for (prop_model_id, values) in rel_values_by_model { for (prop_model_id, values) in rel_values_by_model {
@ -428,11 +433,14 @@ pub(crate) async fn update_form(
// maybe its useful,idk // maybe its useful,idk
context.insert("model", &model); context.insert("model", &model);
context.insert("object", &ObjectDisplay { context.insert(
id: object.id, "object",
model: object.model, &ObjectDisplay {
name: rg.get_object_name(object), id: object.id,
}); model: object.model,
name: rg.get_object_name(object),
},
);
let create_data = prepare_object_create_data(&rg, model.id)?; let create_data = prepare_object_create_data(&rg, model.id)?;
@ -448,7 +456,8 @@ pub(crate) async fn update_form(
.map(|p| p.id) .map(|p| p.id)
.collect_vec(); .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| { prop_ids.into_iter().for_each(|id| {
value_map.insert( value_map.insert(
@ -472,7 +481,7 @@ pub(crate) async fn update_form(
let mut relations_grouped_by_model = relations let mut relations_grouped_by_model = relations
.iter() .iter()
.into_group_map_by(|relation| relation.model); .group_by_model();
let mut property_models_grouped_by_parent = let mut property_models_grouped_by_parent =
rg.get_grouped_prop_models_for_parents(relation_model_ids.clone()); 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 let mut rel_values = relation_values_grouped_by_instance
.remove(&rel.id) .remove(&rel.id)
.unwrap_or_default() .unwrap_or_default()
.into_iter() .group_by_model();
.into_group_map_by(|relation| relation.model);
prop_models_for_relation.iter().for_each(|prop_model| { prop_models_for_relation.iter().for_each(|prop_model| {
relation_values_map.insert( relation_values_map.insert(
@ -525,7 +533,7 @@ pub(crate) async fn update_form(
let object = EnrichedObject { let object = EnrichedObject {
id: object.id, id: object.id,
model: object.model, model: object.model,
name: rg.get_object_name_by_id(object.id), name: rg.get_object_name(object),
values: value_map, values: value_map,
relations: relation_map, relations: relation_map,
}; };
@ -558,7 +566,11 @@ pub(crate) async fn update(
Ok(_id) => { Ok(_id) => {
wg.persist().err_to_500()?; wg.persist().err_to_500()?;
debug!("Object created, redirecting to root"); 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()) Ok(HttpResponse::Ok().finish())
} }
Err(e) => { Err(e) => {

@ -22,6 +22,7 @@ itertools = "0.10.0"
#lazy_static = "1.4.0" #lazy_static = "1.4.0"
bincode = "1.3.1" bincode = "1.3.1"
atomic = "0.5.0" atomic = "0.5.0"
rand = "0.8.3"
[features] [features]
default = [] 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 //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> { impl<T> IsNoneOrElse<T> for Option<T> {
fn is_none_or_else(&self, test: impl FnOnce(&T) -> bool) -> bool { fn is_none_or_else(&self, test: impl FnOnce(&T) -> bool) -> bool {
match self { match self {
None => true, None => true,
Some(value) => { Some(value) => test(value),
test(value)
}
} }
} }
} }

@ -1,6 +1,6 @@
//! Data value structs //! Data value structs
use std::borrow::{Cow, Borrow}; use std::borrow::{Borrow, Cow};
use serde::{Deserialize, Serialize}; 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 { pub trait HaveId {
fn get_id(&self) -> ID; 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 { impl InsertObj {
pub fn new( pub fn new(model_id: ID, values: Vec<InsertValue>, relations: Vec<InsertRel>) -> Self {
model_id: ID,
values: Vec<InsertValue>,
relations: Vec<InsertRel>,
) -> Self {
Self { Self {
model: model_id, model: model_id,
values, values,

@ -1,7 +1,8 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
// #[macro_use] #[cfg(test)]
// extern crate serde_json; #[macro_use]
extern crate serde_json;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
@ -21,10 +22,11 @@ use insert::InsertValue;
pub use model::DataType; pub use model::DataType;
use model::ObjectModel; use model::ObjectModel;
use crate::cool::IsNoneOrElse;
use crate::data::Object;
use crate::helpers::{GroupByModel, GroupByParent};
use crate::model::{PropertyModel, RelationModel}; use crate::model::{PropertyModel, RelationModel};
use crate::update::{UpdateObj, UpsertValue}; use crate::update::{UpdateObj, UpsertValue};
use crate::cool::IsNoneOrElse;
use crate::data::{Object, Value};
mod cool; mod cool;
pub mod data; pub mod data;
@ -33,6 +35,8 @@ pub mod insert;
pub mod model; pub mod model;
pub mod update; pub mod update;
pub mod helpers;
mod serde_atomic_id; mod serde_atomic_id;
mod serde_map_as_list; mod serde_map_as_list;
#[cfg(test)] #[cfg(test)]
@ -40,8 +44,8 @@ mod tests;
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub const YOPA_MAGIC : &[u8; 4] = b"YOPA"; pub const YOPA_MAGIC: &[u8; 4] = b"YOPA";
pub const BINARY_FORMAT : u8 = 1; pub const BINARY_FORMAT: u8 = 1;
/// Stupid storage with naive inefficient file persistence /// Stupid storage with naive inefficient file persistence
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
@ -62,7 +66,7 @@ pub struct Storage {
#[cfg(not(feature = "uuid-ids"))] #[cfg(not(feature = "uuid-ids"))]
#[serde(with = "serde_atomic_id")] #[serde(with = "serde_atomic_id")]
next_id : atomic::Atomic<ID>, next_id: atomic::Atomic<ID>,
#[serde(skip)] #[serde(skip)]
opts: StoreOpts, opts: StoreOpts,
@ -180,7 +184,7 @@ impl Storage {
let parsed: Self = match self.opts.file_format { let parsed: Self = match self.opts.file_format {
FileEncoding::JSON => serde_json::from_reader(reader)?, FileEncoding::JSON => serde_json::from_reader(reader)?,
FileEncoding::BINCODE => { FileEncoding::BINCODE => {
let mut magic : [u8; 5] = [0; 5]; let mut magic: [u8; 5] = [0; 5];
reader.read_exact(&mut magic)?; reader.read_exact(&mut magic)?;
if &magic[0..4] != YOPA_MAGIC { if &magic[0..4] != YOPA_MAGIC {
@ -193,7 +197,7 @@ impl Storage {
} }
bincode::deserialize_from(reader)? bincode::deserialize_from(reader)?
}, }
}; };
let opts = std::mem::replace(&mut self.opts, StoreOpts::default()); let opts = std::mem::replace(&mut self.opts, StoreOpts::default());
@ -231,7 +235,7 @@ impl Storage {
writer.write_all(YOPA_MAGIC)?; writer.write_all(YOPA_MAGIC)?;
writer.write_all(&[BINARY_FORMAT])?; writer.write_all(&[BINARY_FORMAT])?;
bincode::serialize_into(writer, self)? bincode::serialize_into(writer, self)?
}, }
}; };
} }
} }
@ -253,7 +257,7 @@ impl Storage {
} }
/// Get object name, or the ID as string if no name is configured /// 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) { if let Some(o) = self.get_object(id) {
return self.get_object_name(o); return self.get_object_name(o);
} }
@ -261,9 +265,17 @@ impl Storage {
return id.to_string().into(); return id.to_string().into();
} }
pub fn get_object_name<'b, 'a: 'b>(&'a self, object : &'a Object) -> Cow<'b, str> { 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(ObjectModel {
if let Some(v) = self.values.values().find(|v| v.object == object.id && &v.model == name_property) { 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(); return v.value.to_cow();
} }
} }
@ -293,7 +305,9 @@ impl Storage {
/// Iterate object models with given IDs /// Iterate object models with given IDs
pub fn get_object_models_by_ids(&self, ids: Vec<ID>) -> impl Iterator<Item = &ObjectModel> { 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 /// Get an object model by ID
@ -313,9 +327,7 @@ impl Storage {
/// Get all property models grouped by the parent object ID. /// Get all property models grouped by the parent object ID.
pub fn get_grouped_prop_models(&self) -> HashMap<ID, Vec<&PropertyModel>> { pub fn get_grouped_prop_models(&self) -> HashMap<ID, Vec<&PropertyModel>> {
self.prop_models self.prop_models.values().group_by_parent()
.values()
.into_group_map_by(|model| model.object)
} }
/// Get property models belonging to a group of parent IDs, /// Get property models belonging to a group of parent IDs,
@ -327,7 +339,7 @@ impl Storage {
self.prop_models self.prop_models
.values() .values()
.filter(|p| parent_model_ids.contains(&p.object)) .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 /// 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 /// Get all relation models, grouped by their source object model ID
pub fn get_grouped_relation_models(&self) -> HashMap<ID, Vec<&RelationModel>> { pub fn get_grouped_relation_models(&self) -> HashMap<ID, Vec<&RelationModel>> {
self.rel_models self.rel_models.values().group_by_parent()
.values()
.into_group_map_by(|model| model.object)
} }
/// Get reciprocal relation models, grouped by their destination model ID /// Get reciprocal relation models, grouped by their destination model ID
@ -411,7 +421,7 @@ impl Storage {
self.values self.values
.values() .values()
.filter(move |prop| parents.contains(&prop.object)) .filter(move |prop| parents.contains(&prop.object))
.into_group_map_by(|model| model.object) .group_by_parent()
} }
/// Get all objects belonging to a model. /// Get all objects belonging to a model.
@ -432,9 +442,7 @@ impl Storage {
/// Get all objects, grouped by their model ID /// Get all objects, grouped by their model ID
pub fn get_grouped_objects(&self) -> HashMap<ID, Vec<&data::Object>> { pub fn get_grouped_objects(&self) -> HashMap<ID, Vec<&data::Object>> {
self.objects self.objects.values().group_by_model()
.values()
.into_group_map_by(|object| object.model)
} }
/// Get object by ID /// Get object by ID
@ -830,7 +838,7 @@ impl Storage {
parent_id: ID, parent_id: ID,
parent_model_id: ID| parent_model_id: ID|
-> Result<Vec<data::Value>, StorageError> { -> 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![]; let mut values_to_insert = vec![];
for (prop_model_id, prop) in self for (prop_model_id, prop) in self
@ -873,7 +881,7 @@ impl Storage {
first.value, first.value,
self.get_model_name(*prop_model_id), self.get_model_name(*prop_model_id),
) )
.into(), .into(),
)); ));
} }
} }
@ -908,10 +916,7 @@ impl Storage {
let mut values_to_insert = find_values_to_insert(insobj.values, object_id, obj_model_id)?; let mut values_to_insert = find_values_to_insert(insobj.values, object_id, obj_model_id)?;
// And now ..... relations! // And now ..... relations!
let mut relations_by_id = insobj let mut relations_by_id = insobj.relations.group_by_model();
.relations
.into_iter()
.into_group_map_by(|ir| ir.model);
let mut relations_to_insert = vec![]; let mut relations_to_insert = vec![];
for (relation_model_id, relation_model) in self for (relation_model_id, relation_model) in self
@ -1020,7 +1025,7 @@ impl Storage {
), ),
StorageError, 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 values_to_insert = vec![];
let mut ids_to_delete = vec![]; let mut ids_to_delete = vec![];
@ -1028,7 +1033,7 @@ impl Storage {
.values .values
.values() .values()
.filter(|v| v.object == parent_id) .filter(|v| v.object == parent_id)
.into_group_map_by(|v| v.model); .group_by_model();
for (prop_model_id, prop) in self for (prop_model_id, prop) in self
.prop_models .prop_models
@ -1071,7 +1076,7 @@ impl Storage {
first.value, first.value,
self.get_model_name(*prop_model_id), self.get_model_name(*prop_model_id),
) )
.into(), .into(),
)); ));
} }
} }
@ -1113,10 +1118,7 @@ impl Storage {
find_values_to_change(updobj.values, updated_object_id, updated_object_model_id)?; find_values_to_change(updobj.values, updated_object_id, updated_object_model_id)?;
// And now ..... relations! // And now ..... relations!
let mut relations_by_model = updobj let mut relations_by_model = updobj.relations.group_by_model();
.relations
.into_iter()
.into_group_map_by(|ir| ir.model);
let mut relations_to_insert = vec![]; let mut relations_to_insert = vec![];
let mut relations_to_delete = vec![]; let mut relations_to_delete = vec![];
@ -1124,7 +1126,7 @@ impl Storage {
.relations .relations
.values() .values()
.filter(|v| v.object == updated_object_id) .filter(|v| v.object == updated_object_id)
.into_group_map_by(|v| v.model); .group_by_model();
let rel_models_by_id = self let rel_models_by_id = self
.rel_models .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()); debug!("Add {} new object relations", relations_to_insert.len());
for rel in relations_to_insert { for rel in relations_to_insert {
self.relations.insert(rel.id, rel); self.relations.insert(rel.id, rel);

@ -1,20 +1,19 @@
//! Serialize atomic ID by fetching its value //! Serialize atomic ID by fetching its value
use crate::id::ID; 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> pub fn serialize<S>(x: &atomic::Atomic<ID>, s: S) -> Result<S::Ok, S::Error>
where where
S: serde::Serializer, S: serde::Serializer,
{ {
x.load(atomic::Ordering::Relaxed) x.load(atomic::Ordering::Relaxed).serialize(s)
.serialize(s)
} }
pub fn deserialize<'de, D>(deserializer: D) -> Result<atomic::Atomic<ID>, D::Error> pub fn deserialize<'de, D>(deserializer: D) -> Result<atomic::Atomic<ID>, D::Error>
where where
D: Deserializer<'de> D: Deserializer<'de>,
{ {
let id : ID = ID::deserialize(deserializer)?; let id: ID = ID::deserialize(deserializer)?;
Ok(atomic::Atomic::new(id)) Ok(atomic::Atomic::new(id))
} }

@ -5,6 +5,7 @@ fn test1() {
let model = crate::model::ObjectModel { let model = crate::model::ObjectModel {
id: Default::default(), id: Default::default(),
name: "Name".to_string(), name: "Name".to_string(),
name_property: None,
}; };
println!("{}", serde_json::to_string_pretty(&model).unwrap()); println!("{}", serde_json::to_string_pretty(&model).unwrap());

Loading…
Cancel
Save