|
|
@ -1,62 +1,25 @@ |
|
|
|
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 std::borrow::Cow; |
|
|
|
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
|
|
|
|
|
|
|
use insert::InsertValue; |
|
|
|
use itertools::Itertools; |
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
|
|
|
|
use thiserror::Error; |
|
|
|
mod cool; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(feature="uuid-ids")] |
|
|
|
|
|
|
|
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() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(not(feature="uuid-ids"))] |
|
|
|
|
|
|
|
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<u64> = 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 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use cool::{KVVecToKeysOrValues, map_drain_filter}; |
|
|
|
pub use id::ID; |
|
|
|
pub use id::ID; |
|
|
|
use id::next_id; |
|
|
|
use id::next_id; |
|
|
|
|
|
|
|
use insert::InsertObj; |
|
|
|
|
|
|
|
use insert::InsertValue; |
|
|
|
|
|
|
|
use model::ObjectModel; |
|
|
|
|
|
|
|
|
|
|
|
pub mod model; |
|
|
|
pub mod model; |
|
|
|
pub mod data; |
|
|
|
pub mod data; |
|
|
|
pub mod insert; |
|
|
|
pub mod insert; |
|
|
|
|
|
|
|
pub mod id; |
|
|
|
|
|
|
|
mod cool; |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default)] |
|
|
|
/// Stupid storage with no persistence
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)] |
|
|
|
pub struct InMemoryStorage { |
|
|
|
pub struct InMemoryStorage { |
|
|
|
obj_models: HashMap<ID, model::ObjectModel>, |
|
|
|
obj_models: HashMap<ID, model::ObjectModel>, |
|
|
|
rel_models: HashMap<ID, model::RelationModel>, |
|
|
|
rel_models: HashMap<ID, model::RelationModel>, |
|
|
@ -67,7 +30,7 @@ pub struct InMemoryStorage { |
|
|
|
properties: HashMap<ID, data::Value>, |
|
|
|
properties: HashMap<ID, data::Value>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug,Error)] |
|
|
|
#[derive(Debug, Error)] |
|
|
|
pub enum StorageError { |
|
|
|
pub enum StorageError { |
|
|
|
#[error("Referenced {0} does not exist")] |
|
|
|
#[error("Referenced {0} does not exist")] |
|
|
|
NotExist(Cow<'static, str>), |
|
|
|
NotExist(Cow<'static, str>), |
|
|
@ -76,11 +39,13 @@ pub enum StorageError { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl InMemoryStorage { |
|
|
|
impl InMemoryStorage { |
|
|
|
|
|
|
|
/// Create empty store
|
|
|
|
pub fn new() -> Self { |
|
|
|
pub fn new() -> Self { |
|
|
|
Self::default() |
|
|
|
Self::default() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn define_object(&mut self, mut tpl : model::ObjectModel) -> Result<ID, StorageError> { |
|
|
|
/// 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_tpl_id { |
|
|
|
if !self.obj_models.contains_key(&pid) { |
|
|
|
if !self.obj_models.contains_key(&pid) { |
|
|
|
return Err(StorageError::NotExist(format!("parent object model {}", pid).into())); |
|
|
|
return Err(StorageError::NotExist(format!("parent object model {}", pid).into())); |
|
|
@ -97,6 +62,7 @@ impl InMemoryStorage { |
|
|
|
Ok(id) |
|
|
|
Ok(id) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Define a relation between two data objects
|
|
|
|
pub fn define_relation(&mut self, mut rel: model::RelationModel) -> Result<ID, StorageError> { |
|
|
|
pub fn define_relation(&mut self, mut rel: model::RelationModel) -> Result<ID, StorageError> { |
|
|
|
if !self.obj_models.contains_key(&rel.object_tpl_id) { |
|
|
|
if !self.obj_models.contains_key(&rel.object_tpl_id) { |
|
|
|
return Err(StorageError::NotExist(format!("source object model {}", rel.object_tpl_id).into())); |
|
|
|
return Err(StorageError::NotExist(format!("source object model {}", rel.object_tpl_id).into())); |
|
|
@ -116,6 +82,7 @@ impl InMemoryStorage { |
|
|
|
Ok(id) |
|
|
|
Ok(id) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Define a property attached to an object or a relation
|
|
|
|
pub fn define_property(&mut self, mut prop: model::PropertyModel) -> Result<ID, StorageError> { |
|
|
|
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.parent_tpl_id) { |
|
|
|
// Maybe it's attached to a relation?
|
|
|
|
// Maybe it's attached to a relation?
|
|
|
@ -143,7 +110,8 @@ impl InMemoryStorage { |
|
|
|
Ok(id) |
|
|
|
Ok(id) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn undefine_object(&mut self, id : ID) -> Result<ObjectModel, StorageError> { |
|
|
|
/// Delete an object definition and associated data
|
|
|
|
|
|
|
|
pub fn undefine_object(&mut self, id: ID) -> Result<ObjectModel, StorageError> { |
|
|
|
return if let Some(t) = self.obj_models.remove(&id) { |
|
|
|
return if let Some(t) = self.obj_models.remove(&id) { |
|
|
|
// Remove relation templates
|
|
|
|
// 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_tpl_id == id || v.related_tpl_id == id) |
|
|
@ -167,10 +135,11 @@ impl InMemoryStorage { |
|
|
|
Ok(t) |
|
|
|
Ok(t) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Err(StorageError::NotExist(format!("object model {}", id).into())) |
|
|
|
Err(StorageError::NotExist(format!("object model {}", id).into())) |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn undefine_relation(&mut self, id : ID) -> Result<model::RelationModel, StorageError> { |
|
|
|
/// Delete a relation definition and associated data
|
|
|
|
|
|
|
|
pub fn undefine_relation(&mut self, id: ID) -> Result<model::RelationModel, StorageError> { |
|
|
|
return if let Some(t) = self.rel_models.remove(&id) { |
|
|
|
return if let Some(t) = self.rel_models.remove(&id) { |
|
|
|
// Remove relations
|
|
|
|
// 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 == id).keys(); |
|
|
@ -185,22 +154,23 @@ impl InMemoryStorage { |
|
|
|
Ok(t) |
|
|
|
Ok(t) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Err(StorageError::NotExist(format!("relation model {}", id).into())) |
|
|
|
Err(StorageError::NotExist(format!("relation model {}", id).into())) |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn undefine_property(&mut self, id : ID) -> Result<model::PropertyModel, StorageError> { |
|
|
|
/// Delete a property definition and associated data
|
|
|
|
|
|
|
|
pub fn undefine_property(&mut self, id: ID) -> Result<model::PropertyModel, StorageError> { |
|
|
|
return if let Some(t) = self.prop_models.remove(&id) { |
|
|
|
return if let Some(t) = self.prop_models.remove(&id) { |
|
|
|
// Remove relations
|
|
|
|
// 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 == id); |
|
|
|
Ok(t) |
|
|
|
Ok(t) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Err(StorageError::NotExist(format!("property model {}", id).into())) |
|
|
|
Err(StorageError::NotExist(format!("property model {}", id).into())) |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DATA
|
|
|
|
// DATA
|
|
|
|
|
|
|
|
|
|
|
|
/// Insert object with relations, validating the data model
|
|
|
|
/// Insert object with relations, validating the data model constraints
|
|
|
|
pub fn insert_object(&mut self, insobj: InsertObj) -> Result<ID, StorageError> { |
|
|
|
pub fn insert_object(&mut self, insobj: InsertObj) -> Result<ID, StorageError> { |
|
|
|
let obj_model_id = insobj.model_id; |
|
|
|
let obj_model_id = insobj.model_id; |
|
|
|
|
|
|
|
|
|
|
@ -212,10 +182,10 @@ impl InMemoryStorage { |
|
|
|
let object_id = next_id(); |
|
|
|
let object_id = next_id(); |
|
|
|
let object = data::Object { |
|
|
|
let object = data::Object { |
|
|
|
id: object_id, |
|
|
|
id: object_id, |
|
|
|
model_id: obj_model_id |
|
|
|
model_id: obj_model_id, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let find_values_to_insert = |values : Vec<InsertValue>, parent_id : ID| -> Result<Vec<data::Value>, StorageError> { |
|
|
|
let find_values_to_insert = |values: Vec<InsertValue>, parent_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_by_id = values.into_iter().into_group_map_by(|iv| iv.model_id); |
|
|
|
let mut values_to_insert = vec![]; |
|
|
|
let mut values_to_insert = vec![]; |
|
|
|
|
|
|
|
|
|
|
@ -230,7 +200,7 @@ impl InMemoryStorage { |
|
|
|
object_id, |
|
|
|
object_id, |
|
|
|
model_id: prop.id, |
|
|
|
model_id: prop.id, |
|
|
|
value: val_instance.value.cast_to(prop.data_type) |
|
|
|
value: val_instance.value.cast_to(prop.data_type) |
|
|
|
.map_err(|v| StorageError::ConstraintViolation(format!("{} cannot accept value {:?}", prop, v).into()))? |
|
|
|
.map_err(|v| StorageError::ConstraintViolation(format!("{} cannot accept value {:?}", prop, v).into()))?, |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -240,7 +210,7 @@ impl InMemoryStorage { |
|
|
|
id: next_id(), |
|
|
|
id: next_id(), |
|
|
|
object_id, |
|
|
|
object_id, |
|
|
|
model_id: prop.id, |
|
|
|
model_id: prop.id, |
|
|
|
value: def.clone() |
|
|
|
value: def.clone(), |
|
|
|
}); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return Err(StorageError::ConstraintViolation(format!("{} is required for {} (and no default value is defined)", prop, obj_model).into())); |
|
|
|
return Err(StorageError::ConstraintViolation(format!("{} is required for {} (and no default value is defined)", prop, obj_model).into())); |
|
|
@ -278,7 +248,7 @@ impl InMemoryStorage { |
|
|
|
id: next_id(), |
|
|
|
id: next_id(), |
|
|
|
object_id, |
|
|
|
object_id, |
|
|
|
model_id: rel_instance.model_id, |
|
|
|
model_id: rel_instance.model_id, |
|
|
|
related_id: rel_instance.related_id |
|
|
|
related_id: rel_instance.related_id, |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|