#[ 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 < 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 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 < ID , StorageError > {
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 < ID , StorageError > {
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 < ObjectModel , StorageError > {
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 < model ::RelationModel , StorageError > {
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 < model ::PropertyModel , StorageError > {
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 < ID , StorageError > {
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 < 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_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 < 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 )
}
pub fn get_grouped_reciprocal_relation_models ( & self ) -> HashMap < ID , Vec < & RelationModel > > {
self . rel_models . values ( )
. into_group_map_by ( | model | model . related )
}
// Updates
pub fn update_object ( & mut self , model : ObjectModel ) -> Result < ( ) , StorageError > {
if ! self . obj_models . contains_key ( & model . id ) {
return Err ( StorageError ::NotExist ( format! ( "Object model ID {} does not exist." , model . id ) . into ( ) ) ) ;
}
if let Some ( conflict ) = self . obj_models . values ( ) . find ( | m | m . id ! = model . id & & m . name = = model . name ) {
return Err ( StorageError ::ConstraintViolation ( format! ( "Object {} already has the name {}" , conflict . id , model . name ) . into ( ) ) ) ;
}
self . obj_models . insert ( model . id , model ) ;
Ok ( ( ) )
}
pub fn update_relation ( & mut self , mut rel : RelationModel ) -> Result < ( ) , StorageError > {
// Object and Related can't be changed, so we re-fill them from the existing model
if let Some ( existing ) = self . rel_models . get ( & rel . id ) {
rel . object = existing . object ;
rel . related = existing . related ;
} else {
return Err ( StorageError ::NotExist ( format! ( "Relation model ID {} does not exist." , rel . id ) . into ( ) ) ) ;
}
// Difficult checks ...
// yes this is stupid and inefficient and slow and
if let Some ( ( _ , colliding ) ) = self . rel_models . iter ( ) . find ( | ( _ , other ) | {
( other . name = = rel . name & & other . object = = rel . object & & rel . id ! = other . id ) // Exact match
| | ( other . name = = rel . reciprocal_name & & other . object = = rel . related & & rel . id ! = other . id ) // Our reciprocal name collides with related's own relation name
| | ( other . reciprocal_name = = rel . name & & other . related = = rel . object & & rel . id ! = other . id ) // Our name name collides with a reciprocal name on the other relation
| | ( other . reciprocal_name = = rel . reciprocal_name & & other . related = = rel . related & & rel . id ! = other . id ) // 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 ( ) ) ) ;
}
self . rel_models . insert ( rel . id , rel ) ;
Ok ( ( ) )
}
}