@ -1,21 +1,37 @@
use std ::collections ::{ HashMap } ;
use std ::collections ::{ HashMap } ;
use crate ::yopa ::model ::{ ObjectTemplate , ID } ;
use thiserror ::Error ;
use thiserror ::Error ;
use crate ::cool ::{ map_drain_filter , KVVecToKeysOrValues } ;
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 ;
/// Common identifier type
#[ allow(non_camel_case_types) ]
pub type ID = uuid ::Uuid ;
/// Data model structs and enums
/// Data model structs and enums
pub mod model {
pub mod model {
use serde ::{ Serialize , Deserialize } ;
use serde ::{ Serialize , Deserialize } ;
use std ::borrow ::Cow ;
use std ::borrow ::Cow ;
use super ::ID ;
use super ::data ::TypedValue ;
use std ::fmt ::{ Display , Formatter } ;
use std ::fmt ;
/// Common identifier type
/// Get a description of a struct
#[ allow(non_camel_case_types) ]
pub trait Describe {
pub type ID = uuid ::Uuid ;
/// Short but informative description for error messages
fn describe ( & self ) -> String ;
}
/// Object template
/// Object template
#[ derive(Debug,Clone,Serialize,Deserialize) ]
#[ derive(Debug,Clone,Serialize,Deserialize) ]
pub struct ObjectTemplate {
pub struct ObjectModel {
/// PK
/// PK
pub id : ID ,
pub id : ID ,
/// Template name, unique within the database
/// Template name, unique within the database
@ -26,7 +42,7 @@ pub mod model {
/// Relation between templates
/// Relation between templates
#[ derive(Debug,Clone,Serialize,Deserialize) ]
#[ derive(Debug,Clone,Serialize,Deserialize) ]
pub struct RelationTemplate {
pub struct RelationModel {
/// PK
/// PK
pub id : ID ,
pub id : ID ,
/// Object template ID
/// Object template ID
@ -43,7 +59,7 @@ pub mod model {
/// Property definition
/// Property definition
#[ derive(Debug,Clone,Serialize,Deserialize) ]
#[ derive(Debug,Clone,Serialize,Deserialize) ]
pub struct PropertyTemplate {
pub struct PropertyModel {
/// PK
/// PK
pub id : ID ,
pub id : ID ,
/// Object or Reference template ID
/// Object or Reference template ID
@ -61,7 +77,7 @@ pub mod model {
}
}
/// Value data type
/// Value data type
#[ derive(Debug,Clone,Serialize,Deserialize) ]
#[ derive(Debug,Clone,Copy, Serialize,Deserialize,Eq,PartialEq ) ]
pub enum DataType {
pub enum DataType {
/// Text
/// Text
String ,
String ,
@ -73,8 +89,36 @@ pub mod model {
Boolean ,
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 super ::ID ;
use std ::borrow ::Cow ;
use super ::StorageError ;
use std ::num ::ParseIntError ;
use crate ::yopa ::model ::DataType ;
/// Value of a particular type
/// Value of a particular type
#[ derive(Debug,Clone,Serialize,Deserialize) ]
#[ derive(Debug,Clone,Serialize,Deserialize,PartialEq ) ]
pub enum TypedValue {
pub enum TypedValue {
/// Text
/// Text
String ( Cow < ' static , str > ) ,
String ( Cow < ' static , str > ) ,
@ -85,12 +129,142 @@ pub mod model {
/// Boolean yes/no
/// Boolean yes/no
Boolean ( bool ) ,
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 < TypedValue , TypedValue > {
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 ::< i64 > ( ) {
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 ::< f64 > ( ) {
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 )
}
}
}
}
}
/// Data value structs
#[ cfg(test) ]
pub mod data {
mod tests {
use serde ::{ Serialize , Deserialize } ;
use super ::TypedValue ;
use crate ::yopa ::model ::{ ID , TypedValue } ;
use crate ::yopa ::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
/// Instance of an object
#[ derive(Debug,Clone,Serialize,Deserialize) ]
#[ derive(Debug,Clone,Serialize,Deserialize) ]
@ -98,7 +272,7 @@ pub mod data {
/// PK
/// PK
pub id : ID ,
pub id : ID ,
/// Object template ID
/// Object template ID
pub object_tp l_id : ID ,
pub mode l_id : ID ,
}
}
/// Relation between two objects
/// Relation between two objects
@ -109,7 +283,7 @@ pub mod data {
/// Source object ID
/// Source object ID
pub object_id : ID ,
pub object_id : ID ,
/// Relation template ID
/// Relation template ID
pub rel_tp l_id : ID ,
pub mode l_id : ID ,
/// Related object ID
/// Related object ID
pub related_id : ID ,
pub related_id : ID ,
}
}
@ -122,21 +296,51 @@ pub mod data {
/// Owning object ID
/// Owning object ID
pub object_id : ID ,
pub object_id : ID ,
/// Property template ID
/// Property template ID
pub prop_tp l_id : ID ,
pub mode l_id : ID ,
/// Property value
/// Property value
pub value : TypedValue ,
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
}
/// Info for inserting a relation
#[ derive(Debug,Clone,Serialize,Deserialize) ]
pub struct InsertRel {
pub model_id : ID ,
pub related_id : ID ,
pub values : Vec < InsertValue >
}
/// Info for inserting a relation
#[ derive(Debug,Clone,Serialize,Deserialize) ]
pub struct InsertObj {
pub model_id : ID ,
pub values : Vec < InsertValue > ,
pub relations : Vec < InsertRel > ,
}
}
#[ derive(Debug, Default) ]
#[ derive(Debug, Default) ]
pub struct InMemoryStorage {
pub struct InMemoryStorage {
tpl_objects : HashMap < ID , model ::ObjectTemplate > ,
obj_model s : HashMap < ID , model ::ObjectModel > ,
tpl_relations : HashMap < ID , model ::RelationTemplate > ,
rel_model s : HashMap < ID , model ::RelationModel > ,
tpl_properties : HashMap < ID , model ::PropertyTemplate > ,
prop_model s : HashMap < ID , model ::PropertyModel > ,
data_objects : HashMap < ID , data ::Object > ,
objects : HashMap < ID , data ::Object > ,
data_relations : HashMap < ID , data ::Relation > ,
relations : HashMap < ID , data ::Relation > ,
data_values : HashMap < ID , data ::Value > ,
properti es : HashMap < ID , data ::Value > ,
}
}
fn next_id ( ) -> ID {
fn next_id ( ) -> ID {
@ -150,9 +354,9 @@ pub fn zero_id() -> ID {
#[ 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 ( & ' static str ) ,
NotExist ( Cow < ' static , str > ) ,
#[ error( " Schema constraint violation: {0} " ) ]
#[ error( " Schema constraint violation: {0} " ) ]
ConstraintViolation ( & ' static str ) ,
ConstraintViolation ( Cow < ' static , str > ) ,
}
}
impl InMemoryStorage {
impl InMemoryStorage {
@ -160,162 +364,224 @@ impl InMemoryStorage {
Self ::default ( )
Self ::default ( )
}
}
pub fn ins ert _object_template ( & mut self , mut tpl : model ::ObjectTemplate ) -> Result < ID , StorageError > {
pub fn def ine_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 . tpl_object s. contains_key ( & pid ) {
if ! self . obj_model s. contains_key ( & pid ) {
return Err ( StorageError ::NotExist ( "parent object template" ) ) ;
return Err ( StorageError ::NotExist ( format! ( "parent object model {}" , pid ) . into ( ) ) ) ;
}
}
}
}
if self . tpl_object s. iter ( ) . find ( | ( _ , t ) | t . name = = tpl . name ) . is_some ( ) {
if self . obj_model s. iter ( ) . find ( | ( _ , t ) | t . name = = tpl . name ) . is_some ( ) {
return Err ( StorageError ::ConstraintViolation ( "object template with this name already exists" ) ) ;
return Err ( StorageError ::ConstraintViolation ( format! ( "object model with the name \"{}\" already exists" , tpl . name ) . into ( ) ) ) ;
}
}
let id = next_id ( ) ;
let id = next_id ( ) ;
tpl . id = id ;
tpl . id = id ;
self . tpl_object s. insert ( id , tpl ) ;
self . obj_model s. insert ( id , tpl ) ;
Ok ( id )
Ok ( id )
}
}
pub fn ins ert _relation_template ( & mut self , mut rel : model ::RelationTemplate ) -> Result < ID , StorageError > {
pub fn def ine_relation( & mut self , mut rel : model ::RelationModel ) -> Result < ID , StorageError > {
if ! self . tpl_object s. contains_key ( & rel . object_tpl_id ) {
if ! self . obj_model s. contains_key ( & rel . object_tpl_id ) {
return Err ( StorageError ::NotExist ( "origin object template" ) ) ;
return Err ( StorageError ::NotExist ( format! ( "source object model {}" , rel . object_tpl_id ) . into ( ) ) ) ;
}
}
if ! self . tpl_object s. contains_key ( & rel . related_tpl_id ) {
if ! self . obj_model s. contains_key ( & rel . related_tpl_id ) {
return Err ( StorageError ::NotExist ( "related object template" ) ) ;
return Err ( StorageError ::NotExist ( format! ( "related object model {}" , rel . related_tpl_id ) . into ( ) ) ) ;
}
}
if self . tpl_relations . 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_tpl_id = = rel . object_tpl_id ) . is_some ( ) {
return Err ( StorageError ::ConstraintViolation ( "relation with this name and parent already exists" ) ) ;
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 ( ) ;
let id = next_id ( ) ;
rel . id = id ;
rel . id = id ;
self . tpl_relation s. insert ( id , rel ) ;
self . rel_model s. insert ( id , rel ) ;
Ok ( id )
Ok ( id )
}
}
pub fn ins ert _property_template ( & mut self , mut prop : model ::PropertyTemplate ) -> Result < ID , StorageError > {
pub fn def ine_property( & mut self , mut prop : model ::PropertyModel ) -> Result < ID , StorageError > {
if ! self . tpl_object s. contains_key ( & prop . parent_tpl_id ) {
if ! self . obj_model s. contains_key ( & prop . parent_tpl_id ) {
// Maybe it's attached to a relation?
// Maybe it's attached to a relation?
if ! self . tpl_relations . contains_key ( & prop . parent_tpl_id ) {
if ! self . rel_models . contains_key ( & prop . parent_tpl_id ) {
return Err ( StorageError ::NotExist ( "object or reference template" ) ) ;
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 ( ) ) ) ;
}
}
if self . tpl_properties . iter ( ) . find ( | ( _ , t ) | t . parent_tpl_id = = prop . parent_tpl_id & & t . name = = prop . name ) . is_some ( ) {
// Ensure the default type is compatible
return Err ( StorageError ::ConstraintViolation ( "property with this name and parent already exists" ) ) ;
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 ( ) ;
let id = next_id ( ) ;
prop . id = id ;
prop . id = id ;
self . tpl_propertie s. insert ( id , prop ) ;
self . prop_model s. insert ( id , prop ) ;
Ok ( id )
Ok ( id )
}
}
pub fn delete_object_template ( & mut self , id : ID ) -> Result < ObjectTemplate , StorageError > {
pub fn undefine_object ( & mut self , id : ID ) -> Result < ObjectModel , StorageError > {
return if let Some ( t ) = self . tpl_object s. remove ( & id ) {
return if let Some ( t ) = self . obj_model s. remove ( & id ) {
// Remove relation templates
// Remove relation templates
let removed_relation_ids = map_drain_filter ( & mut self . tpl_relation s, | _k , v | v . object_tpl_id = = id | | v . related_tpl_id = = id )
let removed_relation_ids = map_drain_filter ( & mut self . rel_model s, | _k , v | v . object_tpl_id = = id | | v . related_tpl_id = = id )
. keys ( ) ;
. keys ( ) ;
// Remove related property templates
// Remove related property templates
let removed_prop_ids = map_drain_filter ( & mut self . tpl_propertie s, | _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_model s, | _k , v | v . parent_tpl_id = = id | | removed_relation_ids . contains ( & v . parent_tpl_id ) )
. keys ( ) ;
. keys ( ) ;
// Remove objects
// Remove objects
let _ = map_drain_filter ( & mut self . data_ objects, | _k , v | v . object_tp l_id = = id ) ;
let _ = map_drain_filter ( & mut self . objects , | _k , v | v . mode l_id = = id ) ;
// Remove property values
// Remove property values
let _ = map_drain_filter ( & mut self . data_valu es, | _k , v | removed_prop_ids . contains ( & v . prop_tp l_id) ) ;
let _ = map_drain_filter ( & mut self . properti es, | _k , v | removed_prop_ids . contains ( & v . mode l_id) ) ;
// Remove relations
// Remove relations
let _ = map_drain_filter ( & mut self . data_ relations, | _k , v | removed_relation_ids . contains ( & v . rel_tp l_id) ) ;
let _ = map_drain_filter ( & mut self . relations , | _k , v | removed_relation_ids . contains ( & v . mode l_id) ) ;
// Related object remain untouched, so there can be a problem with orphans. This is up to the application to deal with.
// Related object remain untouched, so there can be a problem with orphans. This is up to the application to deal with.
Ok ( t )
Ok ( t )
} else {
} else {
Err ( StorageError ::NotExist ( "object template" ) )
Err ( StorageError ::NotExist ( format! ( "object model {}" , id ) . into ( ) ) )
}
}
}
}
pub fn delete_relation_template ( & mut self , id : ID ) -> Result < model ::RelationTemplate , StorageError > {
pub fn undefine_relation ( & mut self , id : ID ) -> Result < model ::RelationModel , StorageError > {
return if let Some ( t ) = self . tpl_relation s. remove ( & id ) {
return if let Some ( t ) = self . rel_model s. remove ( & id ) {
// Remove relations
// Remove relations
let _ = map_drain_filter ( & mut self . data_ relations, | _k , v | v . rel_tp l_id = = id ) . keys ( ) ;
let _ = map_drain_filter ( & mut self . relations , | _k , v | v . mode l_id = = id ) . keys ( ) ;
// Remove related property templates
// Remove related property templates
let removed_prop_tpl_ids = map_drain_filter ( & mut self . tpl_propertie s, | _k , v | v . parent_tpl_id = = id ) . keys ( ) ;
let removed_prop_tpl_ids = map_drain_filter ( & mut self . prop_model s, | _k , v | v . parent_tpl_id = = id ) . keys ( ) ;
let _ = map_drain_filter ( & mut self . data_valu es, | _k , v | removed_prop_tpl_ids . contains ( & v . prop_tp l_id) ) ;
let _ = map_drain_filter ( & mut self . properti es, | _k , v | removed_prop_tpl_ids . contains ( & v . mode l_id) ) ;
// Related object remain untouched, so there can be a problem with orphans. This is up to the application to deal with.
// Related object remain untouched, so there can be a problem with orphans. This is up to the application to deal with.
Ok ( t )
Ok ( t )
} else {
} else {
Err ( StorageError ::NotExist ( "relation template" ) )
Err ( StorageError ::NotExist ( format! ( "relation model {}" , id ) . into ( ) ) )
}
}
}
}
pub fn delete_property_template ( & mut self , id : ID ) -> Result < model ::PropertyTemplate , StorageError > {
pub fn undefine_property ( & mut self , id : ID ) -> Result < model ::PropertyModel , StorageError > {
return if let Some ( t ) = self . tpl_propertie s. remove ( & id ) {
return if let Some ( t ) = self . prop_model s. remove ( & id ) {
// Remove relations
// Remove relations
let _ = map_drain_filter ( & mut self . data_valu es, | _k , v | v . prop_tp l_id = = id ) ;
let _ = map_drain_filter ( & mut self . properti es, | _k , v | v . mode l_id = = id ) ;
Ok ( t )
Ok ( t )
} else {
} else {
Err ( StorageError ::NotExist ( "property template" ) )
Err ( StorageError ::NotExist ( format! ( "property model {}" , id ) . into ( ) ) )
}
}
}
}
// DATA
// DATA
pub fn insert_object ( & mut self , mut obj : data ::Object ) -> Result < ID , StorageError > {
/// Insert object with relations, validating the data model
if ! self . tpl_objects . contains_key ( & obj . object_tpl_id ) {
pub fn insert_object ( & mut self , insobj : InsertObj ) -> Result < ID , StorageError > {
return Err ( StorageError ::NotExist ( "object template" ) ) ;
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 < 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_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 ( ) ) ) ;
}
}
let id = next_id ( ) ;
obj . id = id ;
self . data_objects . insert ( id , obj ) ;
Ok ( id )
}
}
pub fn insert_value ( & mut self , mut obj : data ::Value ) -> Result < ID , StorageError > {
if ! self . tpl_properties . contains_key ( & obj . prop_tpl_id ) {
return Err ( StorageError ::NotExist ( "property template" ) ) ;
}
}
if ! self . data_objects . contains_key ( & obj . object_id ) {
return Err ( StorageError ::NotExist ( "parent object" ) ) ;
}
}
// TODO validate if it already exists
Ok ( values_to_insert )
} ;
let id = next_id ( ) ;
let mut values_to_insert = find_values_to_insert ( insobj . values , obj_model_id ) ? ;
obj . id = id ;
self . data_values . insert ( id , obj ) ;
// And now ..... relations!
Ok ( id )
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 ( ) ) ) ;
}
}
pub fn insert_relation ( & mut self , mut rel : data ::Relation ) -> Result < ID , StorageError > {
for rel_instance in instances {
if ! self . tpl_relations . contains_key ( & rel . rel_tpl_id ) {
if let Some ( related ) = self . objects . get ( & rel_instance . related_id ) {
return Err ( StorageError ::NotExist ( "relation template" ) ) ;
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 ( ) ) ) ;
}
}
}
if ! self . data_objects . contains_key ( & rel . object_id ) {
// Relations can have properties
return Err ( StorageError ::NotExist ( "parent object" ) ) ;
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 ) ;
if ! self . data_objects . contains_key ( & rel . related_id ) {
for rel in relations_to_insert {
return Err ( StorageError ::NotExist ( "related object" ) ) ;
self . relations . insert ( rel . id , rel ) ;
}
}
// TODO validate if it already exists
for value in values_to_insert {
self . properties . insert ( value . id , value ) ;
}
let id = next_id ( ) ;
Ok ( object_id )
rel . id = id ;
self . data_relations . insert ( id , rel ) ;
Ok ( id )
}
}
}
}