|
|
|
@ -1,3 +1,5 @@ |
|
|
|
|
#[macro_use] extern crate serde_json; |
|
|
|
|
|
|
|
|
|
use std::borrow::Cow; |
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
|
|
|
|
@ -11,6 +13,7 @@ use id::next_id; |
|
|
|
|
use insert::InsertObj; |
|
|
|
|
use insert::InsertValue; |
|
|
|
|
use model::ObjectModel; |
|
|
|
|
use serde::ser::SerializeSeq; |
|
|
|
|
|
|
|
|
|
pub mod model; |
|
|
|
|
pub mod data; |
|
|
|
@ -18,18 +21,29 @@ pub mod insert; |
|
|
|
|
pub mod id; |
|
|
|
|
mod cool; |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
mod tests; |
|
|
|
|
mod serde_map_as_list; |
|
|
|
|
|
|
|
|
|
/// Stupid storage with no persistence
|
|
|
|
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)] |
|
|
|
|
pub struct InMemoryStorage { |
|
|
|
|
#[serde(with = "serde_map_as_list")] |
|
|
|
|
obj_models: HashMap<ID, model::ObjectModel>, |
|
|
|
|
#[serde(with = "serde_map_as_list")] |
|
|
|
|
rel_models: HashMap<ID, model::RelationModel>, |
|
|
|
|
#[serde(with = "serde_map_as_list")] |
|
|
|
|
prop_models: HashMap<ID, model::PropertyModel>, |
|
|
|
|
|
|
|
|
|
#[serde(with = "serde_map_as_list")] |
|
|
|
|
objects: HashMap<ID, data::Object>, |
|
|
|
|
#[serde(with = "serde_map_as_list")] |
|
|
|
|
relations: HashMap<ID, data::Relation>, |
|
|
|
|
#[serde(with = "serde_map_as_list")] |
|
|
|
|
properties: HashMap<ID, data::Value>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Error)] |
|
|
|
|
pub enum StorageError { |
|
|
|
|
#[error("Referenced {0} does not exist")] |
|
|
|
@ -46,7 +60,7 @@ impl InMemoryStorage { |
|
|
|
|
|
|
|
|
|
/// Define a data object
|
|
|
|
|
pub fn define_object(&mut self, mut tpl: model::ObjectModel) -> Result<ID, StorageError> { |
|
|
|
|
if let Some(pid) = tpl.parent_tpl_id { |
|
|
|
|
if let Some(pid) = tpl.parent { |
|
|
|
|
if !self.obj_models.contains_key(&pid) { |
|
|
|
|
return Err(StorageError::NotExist(format!("parent object model {}", pid).into())); |
|
|
|
|
} |
|
|
|
@ -64,16 +78,16 @@ impl InMemoryStorage { |
|
|
|
|
|
|
|
|
|
/// Define a relation between two data objects
|
|
|
|
|
pub fn define_relation(&mut self, mut rel: model::RelationModel) -> Result<ID, StorageError> { |
|
|
|
|
if !self.obj_models.contains_key(&rel.object_tpl_id) { |
|
|
|
|
return Err(StorageError::NotExist(format!("source object model {}", rel.object_tpl_id).into())); |
|
|
|
|
if !self.obj_models.contains_key(&rel.object) { |
|
|
|
|
return Err(StorageError::NotExist(format!("source object model {}", rel.object).into())); |
|
|
|
|
} |
|
|
|
|
if !self.obj_models.contains_key(&rel.related_tpl_id) { |
|
|
|
|
return Err(StorageError::NotExist(format!("related object model {}", rel.related_tpl_id).into())); |
|
|
|
|
if !self.obj_models.contains_key(&rel.related) { |
|
|
|
|
return Err(StorageError::NotExist(format!("related object model {}", rel.related).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if self.rel_models.iter().find(|(_, t)| t.name == rel.name && t.object_tpl_id == rel.object_tpl_id).is_some() { |
|
|
|
|
if self.rel_models.iter().find(|(_, t)| t.name == rel.name && t.object == rel.object).is_some() { |
|
|
|
|
return Err(StorageError::ConstraintViolation( |
|
|
|
|
format!("relation with the name \"{}\" and on model {} already exists", rel.name, rel.object_tpl_id).into())); |
|
|
|
|
format!("relation with the name \"{}\" and on model {} already exists", rel.name, rel.object).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let id = next_id(); |
|
|
|
@ -84,16 +98,16 @@ impl InMemoryStorage { |
|
|
|
|
|
|
|
|
|
/// Define a property attached to an object or a relation
|
|
|
|
|
pub fn define_property(&mut self, mut prop: model::PropertyModel) -> Result<ID, StorageError> { |
|
|
|
|
if !self.obj_models.contains_key(&prop.parent_tpl_id) { |
|
|
|
|
if !self.obj_models.contains_key(&prop.object) { |
|
|
|
|
// Maybe it's attached to a relation?
|
|
|
|
|
if !self.rel_models.contains_key(&prop.parent_tpl_id) { |
|
|
|
|
return Err(StorageError::NotExist(format!("object or relation model {}", prop.parent_tpl_id).into())); |
|
|
|
|
if !self.rel_models.contains_key(&prop.object) { |
|
|
|
|
return Err(StorageError::NotExist(format!("object or relation model {}", prop.object).into())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if self.prop_models.iter().find(|(_, t)| t.parent_tpl_id == prop.parent_tpl_id && t.name == prop.name).is_some() { |
|
|
|
|
if self.prop_models.iter().find(|(_, t)| t.object == prop.object && t.name == prop.name).is_some() { |
|
|
|
|
return Err(StorageError::ConstraintViolation( |
|
|
|
|
format!("property with the name \"{}\" already exists on model {}", prop.name, prop.parent_tpl_id).into())); |
|
|
|
|
format!("property with the name \"{}\" already exists on model {}", prop.name, prop.object).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Ensure the default type is compatible
|
|
|
|
@ -114,21 +128,21 @@ impl InMemoryStorage { |
|
|
|
|
pub fn undefine_object(&mut self, id: ID) -> Result<ObjectModel, StorageError> { |
|
|
|
|
return if let Some(t) = self.obj_models.remove(&id) { |
|
|
|
|
// Remove relation templates
|
|
|
|
|
let removed_relation_ids = map_drain_filter(&mut self.rel_models, |_k, v| v.object_tpl_id == id || v.related_tpl_id == id) |
|
|
|
|
let removed_relation_ids = map_drain_filter(&mut self.rel_models, |_k, v| v.object == id || v.related == id) |
|
|
|
|
.keys(); |
|
|
|
|
|
|
|
|
|
// Remove related property templates
|
|
|
|
|
let removed_prop_ids = map_drain_filter(&mut self.prop_models, |_k, v| v.parent_tpl_id == id || removed_relation_ids.contains(&v.parent_tpl_id)) |
|
|
|
|
let removed_prop_ids = map_drain_filter(&mut self.prop_models, |_k, v| v.object == id || removed_relation_ids.contains(&v.object)) |
|
|
|
|
.keys(); |
|
|
|
|
|
|
|
|
|
// Remove objects
|
|
|
|
|
let _ = map_drain_filter(&mut self.objects, |_k, v| v.model_id == id); |
|
|
|
|
let _ = map_drain_filter(&mut self.objects, |_k, v| v.model == id); |
|
|
|
|
|
|
|
|
|
// Remove property values
|
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| removed_prop_ids.contains(&v.model_id)); |
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| removed_prop_ids.contains(&v.model)); |
|
|
|
|
|
|
|
|
|
// Remove relations
|
|
|
|
|
let _ = map_drain_filter(&mut self.relations, |_k, v| removed_relation_ids.contains(&v.model_id)); |
|
|
|
|
let _ = map_drain_filter(&mut self.relations, |_k, v| removed_relation_ids.contains(&v.model)); |
|
|
|
|
|
|
|
|
|
// Related object remain untouched, so there can be a problem with orphans. This is up to the application to deal with.
|
|
|
|
|
|
|
|
|
@ -142,12 +156,12 @@ impl InMemoryStorage { |
|
|
|
|
pub fn undefine_relation(&mut self, id: ID) -> Result<model::RelationModel, StorageError> { |
|
|
|
|
return if let Some(t) = self.rel_models.remove(&id) { |
|
|
|
|
// Remove relations
|
|
|
|
|
let _ = map_drain_filter(&mut self.relations, |_k, v| v.model_id == id).keys(); |
|
|
|
|
let _ = map_drain_filter(&mut self.relations, |_k, v| v.model == id).keys(); |
|
|
|
|
|
|
|
|
|
// Remove related property templates
|
|
|
|
|
let removed_prop_tpl_ids = map_drain_filter(&mut self.prop_models, |_k, v| v.parent_tpl_id == id).keys(); |
|
|
|
|
let removed_prop_tpl_ids = map_drain_filter(&mut self.prop_models, |_k, v| v.object == id).keys(); |
|
|
|
|
|
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| removed_prop_tpl_ids.contains(&v.model_id)); |
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| removed_prop_tpl_ids.contains(&v.model)); |
|
|
|
|
|
|
|
|
|
// Related object remain untouched, so there can be a problem with orphans. This is up to the application to deal with.
|
|
|
|
|
|
|
|
|
@ -161,7 +175,7 @@ impl InMemoryStorage { |
|
|
|
|
pub fn undefine_property(&mut self, id: ID) -> Result<model::PropertyModel, StorageError> { |
|
|
|
|
return if let Some(t) = self.prop_models.remove(&id) { |
|
|
|
|
// Remove relations
|
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| v.model_id == id); |
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| v.model == id); |
|
|
|
|
Ok(t) |
|
|
|
|
} else { |
|
|
|
|
Err(StorageError::NotExist(format!("property model {}", id).into())) |
|
|
|
@ -194,14 +208,14 @@ impl InMemoryStorage { |
|
|
|
|
let object_id = next_id(); |
|
|
|
|
let object = data::Object { |
|
|
|
|
id: object_id, |
|
|
|
|
model_id: obj_model_id, |
|
|
|
|
model: obj_model_id, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let find_values_to_insert = |values: Vec<InsertValue>, parent_model_id: ID| -> Result<Vec<data::Value>, StorageError> { |
|
|
|
|
let find_values_to_insert = |values: Vec<InsertValue>, 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_id); |
|
|
|
|
let mut values_to_insert = vec![]; |
|
|
|
|
|
|
|
|
|
for (id, prop) in self.prop_models.iter().filter(|(_id, p)| p.parent_tpl_id == parent_model_id) { |
|
|
|
|
for (id, prop) in self.prop_models.iter().filter(|(_id, p)| p.object == parent_model_id) { |
|
|
|
|
if let Some(values) = values_by_id.remove(id) { |
|
|
|
|
if values.len() > 1 && !prop.multiple { |
|
|
|
|
return Err(StorageError::ConstraintViolation(format!("{} of {} cannot have multiple values", prop, self.describe_id(parent_model_id)).into())); |
|
|
|
@ -209,8 +223,8 @@ impl InMemoryStorage { |
|
|
|
|
for val_instance in values { |
|
|
|
|
values_to_insert.push(data::Value { |
|
|
|
|
id: next_id(), |
|
|
|
|
object_id, |
|
|
|
|
model_id: prop.id, |
|
|
|
|
object: parent_id, |
|
|
|
|
model: prop.id, |
|
|
|
|
value: val_instance.value.cast_to(prop.data_type) |
|
|
|
|
.map_err(|v| StorageError::ConstraintViolation(format!("{} cannot accept value {:?}", prop, v).into()))?, |
|
|
|
|
}); |
|
|
|
@ -220,8 +234,8 @@ impl InMemoryStorage { |
|
|
|
|
if let Some(def) = &prop.default { |
|
|
|
|
values_to_insert.push(data::Value { |
|
|
|
|
id: next_id(), |
|
|
|
|
object_id, |
|
|
|
|
model_id: prop.id, |
|
|
|
|
object: parent_id, |
|
|
|
|
model: prop.id, |
|
|
|
|
value: def.clone(), |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
@ -234,13 +248,13 @@ impl InMemoryStorage { |
|
|
|
|
Ok(values_to_insert) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let mut values_to_insert = find_values_to_insert(insobj.values, obj_model_id)?; |
|
|
|
|
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_id); |
|
|
|
|
let mut relations_to_insert = vec![]; |
|
|
|
|
|
|
|
|
|
for (relation_model_id, relation_model) in self.rel_models.iter().filter(|(_id, r)| r.object_tpl_id == obj_model_id) { |
|
|
|
|
for (relation_model_id, relation_model) in self.rel_models.iter().filter(|(_id, r)| r.object == obj_model_id) { |
|
|
|
|
if let Some(instances) = relations_by_id.remove(relation_model_id) { |
|
|
|
|
if instances.len() > 1 && !relation_model.multiple { |
|
|
|
|
return Err(StorageError::ConstraintViolation(format!("{} of {} cannot be set multiply", relation_model, obj_model).into())); |
|
|
|
@ -248,23 +262,25 @@ impl InMemoryStorage { |
|
|
|
|
|
|
|
|
|
for rel_instance in instances { |
|
|
|
|
if let Some(related) = self.objects.get(&rel_instance.related_id) { |
|
|
|
|
if related.model_id != relation_model.related_tpl_id { |
|
|
|
|
if related.model != relation_model.related { |
|
|
|
|
return Err(StorageError::ConstraintViolation( |
|
|
|
|
format!("{} of {} requires object of type {}, got {}", |
|
|
|
|
relation_model, obj_model, |
|
|
|
|
self.describe_id(relation_model.related_tpl_id), |
|
|
|
|
self.describe_id(related.model_id)).into())); |
|
|
|
|
self.describe_id(relation_model.related), |
|
|
|
|
self.describe_id(related.model)).into())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let relation_id = next_id(); |
|
|
|
|
|
|
|
|
|
// Relations can have properties
|
|
|
|
|
values_to_insert.extend(find_values_to_insert(rel_instance.values, *relation_model_id)?); |
|
|
|
|
values_to_insert.extend(find_values_to_insert(rel_instance.values, relation_id, *relation_model_id)?); |
|
|
|
|
|
|
|
|
|
relations_to_insert.push(data::Relation { |
|
|
|
|
id: next_id(), |
|
|
|
|
object_id, |
|
|
|
|
model_id: rel_instance.model_id, |
|
|
|
|
related_id: rel_instance.related_id, |
|
|
|
|
id: relation_id, |
|
|
|
|
object: object_id, |
|
|
|
|
model: rel_instance.model_id, |
|
|
|
|
related: rel_instance.related_id, |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|