#[ macro_use ] extern crate serde_json ;
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 < 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 " ) ]
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 < ID , StorageError > {
if let Some ( pid ) = tpl . parent {
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 )
}
/// 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 ) {
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 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 ) . into ( ) ) ) ;
}
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 < ID , StorageError > {
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 ( ) ) )
} ) ;
}
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 < 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 = = 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 < 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 ) . 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 < 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 ) ;
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 < ID , StorageError > {
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 : obj_model_id ,
} ;
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 . 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 < Item = & ObjectModel > {
self . obj_models . values ( )
}
pub fn get_grouped_prop_models ( & self ) -> HashMap < ID , Vec < & PropertyModel > > {
self . prop_models . values ( )
. into_group_map_by ( | model | model . object )
}
pub fn get_grouped_relation_models ( & self ) -> HashMap < ID , Vec < & RelationModel > > {
self . rel_models . values ( )
. into_group_map_by ( | model | model . object )
}
}