#[macro_use] extern crate serde_json; #[macro_use] extern crate log; use std::borrow::Cow; use std::collections::HashMap; use itertools::Itertools; use serde::{Deserialize, Serialize}; use thiserror::Error; use cool::{KVVecToKeysOrValues, map_drain_filter}; pub use id::ID; use id::next_id; use insert::InsertObj; use insert::InsertValue; use model::ObjectModel; use crate::model::{PropertyModel, RelationModel}; pub use data::{TypedValue}; pub use model::{DataType}; pub mod model; pub mod data; 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 Storage { #[serde(with = "serde_map_as_list")] obj_models: HashMap, #[serde(with = "serde_map_as_list")] rel_models: HashMap, #[serde(with = "serde_map_as_list")] prop_models: HashMap, #[serde(with = "serde_map_as_list")] objects: HashMap, #[serde(with = "serde_map_as_list")] relations: HashMap, #[serde(with = "serde_map_as_list")] properties: HashMap, } #[derive(Debug, Error)] pub enum StorageError { #[error("Referenced {0} does not exist")] NotExist(Cow<'static, str>), #[error("Schema constraint violation: {0}")] ConstraintViolation(Cow<'static, str>), } impl Storage { /// Create empty store pub fn new() -> Self { Self::default() } /// Define a data object pub fn define_object(&mut self, mut tpl: model::ObjectModel) -> Result { if tpl.name.is_empty() { return Err(StorageError::ConstraintViolation("name must not be empty".into())); } if self.obj_models.iter().find(|(_, t)| t.name == tpl.name).is_some() { return Err(StorageError::ConstraintViolation(format!("object model with the name \"{}\" already exists", tpl.name).into())); } debug!("Define object model \"{}\"", tpl.name); let id = next_id(); tpl.id = id; self.obj_models.insert(id, tpl); Ok(id) } /// Define a relation between two data objects pub fn define_relation(&mut self, mut rel: model::RelationModel) -> Result { if rel.name.is_empty() { return Err(StorageError::ConstraintViolation("name must not be empty".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) { return Err(StorageError::NotExist(format!("related object model {}", rel.related).into())); } if let Some((_, colliding)) = self.rel_models.iter().find(|(_, other)| { (other.name == rel.name && other.object == rel.object) // Exact match || (other.name == rel.reciprocal_name && other.object == rel.related) // Our reciprocal name collides with related's own relation name || (other.reciprocal_name == rel.name && other.related == rel.object) // Our name name collides with a reciprocal name on the other relation || (other.reciprocal_name == rel.reciprocal_name && other.related == rel.related) // Reciprocal names collide for the same destination }) { return Err(StorageError::ConstraintViolation( format!("name collision (\"{}\" / \"{}\") with existing relation (\"{}\" / \"{}\")", rel.name, rel.reciprocal_name, colliding.name, colliding.reciprocal_name ).into())); } debug!("Define relation model \"{}\" from {} to {}, reciprocal name \"{}\"", rel.name, self.describe_model(rel.object), self.describe_model(rel.related), rel.reciprocal_name); let id = next_id(); rel.id = id; self.rel_models.insert(id, rel); Ok(id) } /// Define a property attached to an object or a relation pub fn define_property(&mut self, mut prop: model::PropertyModel) -> Result { if prop.name.is_empty() { return Err(StorageError::ConstraintViolation("name must not be empty".into())); } if !self.obj_models.contains_key(&prop.object) { // Maybe it's attached to a relation? 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.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.object).into())); } // Ensure the default type is compatible if let Some(d) = prop.default { prop.default = Some(match d.cast_to(prop.data_type) { Ok(v) => v, Err(d) => return Err(StorageError::NotExist(format!("default value {:?} has invalid type", d).into())) }); } debug!("Define property model \"{}\" of {}", prop.name, self.describe_model(prop.object)); let id = next_id(); prop.id = id; self.prop_models.insert(id, prop); Ok(id) } /// Delete an object definition and associated data pub fn undefine_object(&mut self, id: ID) -> Result { return if let Some(t) = self.obj_models.remove(&id) { debug!("Undefine object model \"{}\"", t.name); // Remove relation templates 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.object == id || removed_relation_ids.contains(&v.object)) .keys(); // Remove objects 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)); // Remove relations 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. Ok(t) } else { Err(StorageError::NotExist(format!("object model {}", id).into())) }; } /// Delete a relation definition and associated data pub fn undefine_relation(&mut self, id: ID) -> Result { return if let Some(t) = self.rel_models.remove(&id) { debug!("Undefine relation model \"{}\"", t.name); // Remove relations 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.object == id).keys(); 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. Ok(t) } else { Err(StorageError::NotExist(format!("relation model {}", id).into())) }; } /// Delete a property definition and associated data pub fn undefine_property(&mut self, id: ID) -> Result { return if let Some(t) = self.prop_models.remove(&id) { debug!("Undefine property model \"{}\"", t.name); // Remove relations let _ = map_drain_filter(&mut self.properties, |_k, v| v.model == id); Ok(t) } else { Err(StorageError::NotExist(format!("property model {}", id).into())) }; } pub fn describe_model(&self, id: ID) -> String { if let Some(x) = self.obj_models.get(&id) { x.to_string() } else if let Some(x) = self.rel_models.get(&id) { x.to_string() } else if let Some(x) = self.prop_models.get(&id) { x.to_string() } else { id.to_string() } } pub fn get_model_name(&self, id: ID) -> &str { if let Some(x) = self.obj_models.get(&id) { &x.name } else if let Some(x) = self.rel_models.get(&id) { &x.name } else if let Some(x) = self.prop_models.get(&id) { &x.name } else { "???" } } // DATA /// Insert object with relations, validating the data model constraints pub fn insert_object(&mut self, insobj: InsertObj) -> Result { let obj_model_id = insobj.model_id; debug!("Insert object {:?}", insobj); let obj_model = match self.obj_models.get(&obj_model_id) { Some(m) => m, None => return Err(StorageError::NotExist(format!("object model {}", obj_model_id).into())) }; let object_id = next_id(); let object = data::Object { id: object_id, model: obj_model_id, }; let find_values_to_insert = |values: Vec, parent_id : ID, parent_model_id: ID| -> Result, 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.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_model(parent_model_id)).into())); } for val_instance in values { values_to_insert.push(data::Value { id: next_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()))?, }); } } else { if !prop.optional { if let Some(def) = &prop.default { values_to_insert.push(data::Value { id: next_id(), object: parent_id, model: prop.id, value: def.clone(), }); } else { return Err(StorageError::ConstraintViolation(format!("{} is required for {} and no default value is defined", prop, self.describe_model(parent_model_id)).into())); } } } } Ok(values_to_insert) }; 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 == 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())); } for rel_instance in instances { if let Some(related) = self.objects.get(&rel_instance.related_id) { if related.model != relation_model.related { return Err(StorageError::ConstraintViolation( format!("{} of {} requires object of type {}, got {}", relation_model, obj_model, self.describe_model(relation_model.related), self.describe_model(related.model)).into())); } } let relation_id = next_id(); // Relations can have properties values_to_insert.extend(find_values_to_insert(rel_instance.values, relation_id, *relation_model_id)?); relations_to_insert.push(data::Relation { id: relation_id, object: object_id, model: rel_instance.model_id, related: rel_instance.related_id, }); } } else { if !relation_model.optional { return Err(StorageError::ConstraintViolation(format!("{} is required for {}", relation_model, obj_model).into())); } } } self.objects.insert(object_id, object); for rel in relations_to_insert { self.relations.insert(rel.id, rel); } for value in values_to_insert { self.properties.insert(value.id, value); } Ok(object_id) } // Reading pub fn get_object_models(&self) -> impl Iterator { self.obj_models.values() } pub fn get_object_model(&self, id : ID) -> Option<&ObjectModel> { self.obj_models.get(&id) } pub fn get_relation_model(&self, id : ID) -> Option<&RelationModel> { self.rel_models.get(&id) } pub fn get_prop_model(&self, id : ID) -> Option<&PropertyModel> { self.prop_models.get(&id) } pub fn get_grouped_prop_models(&self) -> HashMap> { self.prop_models.values() .into_group_map_by(|model| model.object) } pub fn get_grouped_relation_models(&self) -> HashMap> { self.rel_models.values() .into_group_map_by(|model| model.object) } }