use std::collections::{HashMap}; use thiserror::Error; use crate::cool::{map_drain_filter, KVVecToKeysOrValues}; use model::{ObjectModel}; use insert::InsertObj; use itertools::Itertools; use std::borrow::Cow; use model::Describe; use insert::InsertValue; use data::TypedValue; mod cool; /* pub mod id { /// Common identifier type #[allow(non_camel_case_types)] pub type ID = uuid::Uuid; pub fn next_id() -> ID { uuid::Uuid::new_v4() } pub fn zero_id() -> ID { uuid::Uuid::nil() } } */ pub mod id { /// Common identifier type #[allow(non_camel_case_types)] pub type ID = u64; lazy_static::lazy_static! { static ref counter: parking_lot::Mutex = parking_lot::Mutex::new(0); } pub fn next_id() -> ID { let mut m = counter.lock(); let v = *m; *m += 1; v } pub fn zero_id() -> ID { 0 } } pub use id::ID; use id::next_id; /// Data model structs and enums pub mod model { use serde::{Serialize, Deserialize}; use std::borrow::Cow; use super::ID; use super::data::TypedValue; use std::fmt::{Display, Formatter}; use std::fmt; /// Get a description of a struct pub trait Describe { /// Short but informative description for error messages fn describe(&self) -> String; } /// Object template #[derive(Debug,Clone,Serialize,Deserialize)] pub struct ObjectModel { /// PK pub id : ID, /// Template name, unique within the database pub name: String, /// Parent object template ID pub parent_tpl_id: Option, } /// Relation between templates #[derive(Debug,Clone,Serialize,Deserialize)] pub struct RelationModel { /// PK pub id: ID, /// Object template ID pub object_tpl_id: ID, /// Relation name, unique within the parent object pub name: String, /// Relation is optional pub optional: bool, /// Relation can be multiple pub multiple: bool, /// Related object template ID pub related_tpl_id: ID, } /// Property definition #[derive(Debug,Clone,Serialize,Deserialize)] pub struct PropertyModel { /// PK pub id: ID, /// Object or Reference template ID pub parent_tpl_id: ID, /// Property name, unique within the parent object or reference pub name: String, /// Property is optional pub optional: bool, /// Property can be multiple pub multiple: bool, /// Property data type pub data_type: DataType, /// Default value, used for newly created objects pub default: Option, } /// Value data type #[derive(Debug,Clone,Copy,Serialize,Deserialize,Eq,PartialEq)] pub enum DataType { /// Text String, /// Integer Integer, /// Floating point number Decimal, /// Boolean yes/no Boolean, } impl Display for ObjectModel { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "object \"{}\" ({})", self.name, self.id) } } impl Display for RelationModel { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "relation \"{}\" ({})", self.name, self.id) } } impl Display for PropertyModel { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "property \"{}\" ({})", self.name, self.id) } } } /// Data value structs pub mod data { use serde::{Serialize, Deserialize}; use crate::ID; use std::borrow::Cow; use crate::StorageError; use std::num::ParseIntError; use crate::model::DataType; /// Value of a particular type #[derive(Debug,Clone,Serialize,Deserialize,PartialEq)] pub enum TypedValue { /// Text String(Cow<'static, str>), /// Integer Integer(i64), /// Floating point number Decimal(f64), /// Boolean yes/no Boolean(bool), } impl TypedValue { /// Try ot cast to another type. On error, the original value is returned as Err. pub fn cast_to(self, ty : DataType) -> Result { match (self, ty) { // to string (s @ TypedValue::String(_), DataType::String) => Ok(s), (TypedValue::Integer(i), DataType::String) => Ok(TypedValue::String(i.to_string().into())), (TypedValue::Decimal(f), DataType::String) => Ok(TypedValue::String(f.to_string().into())), (TypedValue::Boolean(b), DataType::String) => Ok(TypedValue::String(Cow::Borrowed(if b { "1" } else { "0" }))), // to int (TypedValue::String(s), DataType::Integer) => { match s.parse::() { Ok(i) => Ok(TypedValue::Integer(i)), Err(_) => Err(TypedValue::String(s)) } }, (s @ TypedValue::Integer(_), DataType::Integer) => Ok(s), (TypedValue::Decimal(f), DataType::Integer) => Ok(TypedValue::Integer(f.round() as i64)), (TypedValue::Boolean(b), DataType::Integer) => Ok(TypedValue::Integer(if b { 1 } else { 0 })), // to float (TypedValue::String(s), DataType::Decimal) => { match s.parse::() { Ok(i) => Ok(TypedValue::Decimal(i)), Err(_) => Err(TypedValue::String(s)) } }, (TypedValue::Integer(i), DataType::Decimal) => Ok(TypedValue::Decimal(i as f64)), (d @ TypedValue::Decimal(_), DataType::Decimal) => Ok(d), (e @ TypedValue::Boolean(_), DataType::Decimal) => Err(e), // to bool (TypedValue::String(s), DataType::Boolean) => { match &(&s).to_ascii_lowercase()[..] { "y" | "yes" | "true" | "1" => { Ok(TypedValue::Boolean(true)) } "n" | "no" | "false" | "0" => { Ok(TypedValue::Boolean(false)) } _ => { Err(TypedValue::String(s)) } } }, (TypedValue::Integer(i), DataType::Boolean) => Ok(TypedValue::Boolean(i != 0)), (e @ TypedValue::Decimal(_), DataType::Boolean) => Err(e), (b @ TypedValue::Boolean(_), DataType::Boolean) => Ok(b), (s, _) => { Err(s) } } } } #[cfg(test)] mod tests { use crate::TypedValue; use crate::model::DataType; #[test] fn test_cast_to_bool() { // Cast to bool assert_eq!(Ok(TypedValue::Boolean(true)), TypedValue::Boolean(true).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(false)), TypedValue::Boolean(false).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(true)), TypedValue::Integer(123).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(false)), TypedValue::Integer(0).cast_to(DataType::Boolean)); assert_eq!(Err(TypedValue::Decimal(0.0)), TypedValue::Decimal(0.0).cast_to(DataType::Boolean)); assert_eq!(Err(TypedValue::Decimal(123.0)), TypedValue::Decimal(123.0).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(true)), TypedValue::String("true".into()).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(true)), TypedValue::String("1".into()).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(true)), TypedValue::String("y".into()).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(true)), TypedValue::String("yes".into()).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(false)), TypedValue::String("false".into()).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(false)), TypedValue::String("0".into()).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(false)), TypedValue::String("n".into()).cast_to(DataType::Boolean)); assert_eq!(Ok(TypedValue::Boolean(false)), TypedValue::String("no".into()).cast_to(DataType::Boolean)); assert_eq!(Err(TypedValue::String("blorg".into())), TypedValue::String("blorg".into()).cast_to(DataType::Boolean)); } #[test] fn test_cast_to_int() { // Cast to bool assert_eq!(Ok(TypedValue::Integer(1)), TypedValue::Boolean(true).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(0)), TypedValue::Boolean(false).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(123)), TypedValue::Integer(123).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(0)), TypedValue::Integer(0).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(0)), TypedValue::Decimal(0.0).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(123)), TypedValue::Decimal(123.0).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(-124)), TypedValue::Decimal(-123.7).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(123)), TypedValue::String("123".into()).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(-123)), TypedValue::String("-123".into()).cast_to(DataType::Integer)); assert_eq!(Ok(TypedValue::Integer(0)), TypedValue::String("0".into()).cast_to(DataType::Integer)); assert_eq!(Err(TypedValue::String("123.456".into())), TypedValue::String("123.456".into()).cast_to(DataType::Integer)); assert_eq!(Err(TypedValue::String("-123.456".into())), TypedValue::String("-123.456".into()).cast_to(DataType::Integer)); } #[test] fn test_cast_to_decimal() { // Cast to bool assert_eq!(Err(TypedValue::Boolean(true)), TypedValue::Boolean(true).cast_to(DataType::Decimal)); assert_eq!(Err(TypedValue::Boolean(false)), TypedValue::Boolean(false).cast_to(DataType::Decimal)); assert_eq!(Ok(TypedValue::Decimal(123.0)), TypedValue::Integer(123).cast_to(DataType::Decimal)); assert_eq!(Ok(TypedValue::Decimal(0.0)), TypedValue::Integer(0).cast_to(DataType::Decimal)); assert_eq!(Ok(TypedValue::Decimal(0.0)), TypedValue::Decimal(0.0).cast_to(DataType::Decimal)); assert_eq!(Ok(TypedValue::Decimal(-123.7)), TypedValue::Decimal(-123.7).cast_to(DataType::Decimal)); assert_eq!(Ok(TypedValue::Decimal(123.0)), TypedValue::String("123".into()).cast_to(DataType::Decimal)); assert_eq!(Ok(TypedValue::Decimal(-123.0)), TypedValue::String("-123".into()).cast_to(DataType::Decimal)); assert_eq!(Ok(TypedValue::Decimal(0.0)), TypedValue::String("0".into()).cast_to(DataType::Decimal)); assert_eq!(Ok(TypedValue::Decimal(-123.456)), TypedValue::String("-123.456".into()).cast_to(DataType::Decimal)); } #[test] fn test_cast_to_string() { // Cast to bool assert_eq!(Ok(TypedValue::String("1".into())), TypedValue::Boolean(true).cast_to(DataType::String)); assert_eq!(Ok(TypedValue::String("0".into())), TypedValue::Boolean(false).cast_to(DataType::String)); assert_eq!(Ok(TypedValue::String("123".into())), TypedValue::Integer(123).cast_to(DataType::String)); assert_eq!(Ok(TypedValue::String("0".into())), TypedValue::Integer(0).cast_to(DataType::String)); assert_eq!(Ok(TypedValue::String("0".into())), TypedValue::Decimal(0.0).cast_to(DataType::String)); assert_eq!(Ok(TypedValue::String("123".into())), TypedValue::Decimal(123.0).cast_to(DataType::String)); assert_eq!(Ok(TypedValue::String("-123.5".into())), TypedValue::Decimal(-123.5).cast_to(DataType::String)); assert_eq!(Ok(TypedValue::String("blorg".into())), TypedValue::String("blorg".into()).cast_to(DataType::String)); } } /// Instance of an object #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Object { /// PK pub id : ID, /// Object template ID pub model_id: ID, } /// Relation between two objects #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Relation { /// PK pub id : ID, /// Source object ID pub object_id : ID, /// Relation template ID pub model_id: ID, /// Related object ID pub related_id : ID, } /// Value attached to an object #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Value { /// PK pub id : ID, /// Owning object ID pub object_id : ID, /// Property template ID pub model_id: ID, /// Property value pub value : TypedValue, } } /// Helper struct for inserting relational data in the database pub mod insert { use super::ID; use super::data::TypedValue; use serde::{Serialize,Deserialize}; /// Value to insert #[derive(Debug,Clone,Serialize,Deserialize)] pub struct InsertValue { pub model_id: ID, pub value: TypedValue } impl InsertValue { pub fn new(model_id : ID, value : TypedValue) -> Self { Self { model_id, value } } } /// Info for inserting a relation #[derive(Debug,Clone,Serialize,Deserialize)] pub struct InsertRel { pub model_id: ID, pub related_id: ID, pub values: Vec } impl InsertRel { pub fn new(model_id : ID, related_id: ID, values : Vec) -> Self { Self { model_id, related_id, values } } } /// Info for inserting a relation #[derive(Debug,Clone,Serialize,Deserialize)] pub struct InsertObj { pub model_id: ID, pub values: Vec, pub relations: Vec, } impl InsertObj { pub fn new(model_id : ID, values : Vec, relations: Vec) -> Self { Self { model_id, values, relations } } } } #[derive(Debug, Default)] pub struct InMemoryStorage { obj_models: HashMap, rel_models: HashMap, prop_models: HashMap, objects: HashMap, relations: HashMap, 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 InMemoryStorage { pub fn new() -> Self { Self::default() } pub fn define_object(&mut self, mut tpl : model::ObjectModel) -> Result { if let Some(pid) = tpl.parent_tpl_id { if !self.obj_models.contains_key(&pid) { return Err(StorageError::NotExist(format!("parent object model {}", pid).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())); } let id = next_id(); tpl.id = id; self.obj_models.insert(id, tpl); Ok(id) } pub fn define_relation(&mut self, mut rel: model::RelationModel) -> Result { 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.related_tpl_id) { return Err(StorageError::NotExist(format!("related object model {}", rel.related_tpl_id).into())); } if self.rel_models.iter().find(|(_, t)| t.name == rel.name && t.object_tpl_id == rel.object_tpl_id).is_some() { return Err(StorageError::ConstraintViolation( format!("relation with the name \"{}\" and on model {} already exists", rel.name, rel.object_tpl_id).into())); } let id = next_id(); rel.id = id; self.rel_models.insert(id, rel); Ok(id) } pub fn define_property(&mut self, mut prop: model::PropertyModel) -> Result { if !self.obj_models.contains_key(&prop.parent_tpl_id) { // 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.prop_models.iter().find(|(_, t)| t.parent_tpl_id == prop.parent_tpl_id && 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())); } // 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())) }); } let id = next_id(); prop.id = id; self.prop_models.insert(id, prop); Ok(id) } pub fn undefine_object(&mut self, id : ID) -> Result { 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) .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)) .keys(); // Remove objects let _ = map_drain_filter(&mut self.objects, |_k, v| v.model_id == id); // Remove property values let _ = map_drain_filter(&mut self.properties, |_k, v| removed_prop_ids.contains(&v.model_id)); // Remove relations let _ = map_drain_filter(&mut self.relations, |_k, v| removed_relation_ids.contains(&v.model_id)); // 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())) } } pub fn undefine_relation(&mut self, id : ID) -> Result { 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(); // 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 _ = map_drain_filter(&mut self.properties, |_k, v| removed_prop_tpl_ids.contains(&v.model_id)); // 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())) } } pub fn undefine_property(&mut self, id : ID) -> Result { 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); Ok(t) } else { Err(StorageError::NotExist(format!("property model {}", id).into())) } } // DATA /// Insert object with relations, validating the data model pub fn insert_object(&mut self, insobj: InsertObj) -> Result { let obj_model_id = insobj.model_id; 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_id: obj_model_id }; let find_values_to_insert = |values : Vec, parent_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.parent_tpl_id == parent_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, obj_model).into())); } for val_instance in values { values_to_insert.push(data::Value { id: next_id(), object_id, model_id: 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_id, model_id: prop.id, value: def.clone() }); } else { return Err(StorageError::ConstraintViolation(format!("{} is required for {} (and no default value is defined)", prop, obj_model).into())); } } } } Ok(values_to_insert) }; let mut values_to_insert = find_values_to_insert(insobj.values, 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) { 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_id != relation_model.related_tpl_id { return Err(StorageError::ConstraintViolation(format!("{} of {} requires object of type {}, got {}", relation_model, obj_model, relation_model.related_tpl_id, related.model_id).into())); } } // Relations can have properties values_to_insert.extend(find_values_to_insert(rel_instance.values, *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 }); } } 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) } }