commit
b4dbb94e27
@ -0,0 +1,2 @@ |
|||||||
|
/target |
||||||
|
.idea/ |
@ -0,0 +1,222 @@ |
|||||||
|
# This file is automatically @generated by Cargo. |
||||||
|
# It is not intended for manual editing. |
||||||
|
[[package]] |
||||||
|
name = "anyhow" |
||||||
|
version = "1.0.38" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "cfg-if" |
||||||
|
version = "1.0.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "getrandom" |
||||||
|
version = "0.2.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" |
||||||
|
dependencies = [ |
||||||
|
"cfg-if", |
||||||
|
"libc", |
||||||
|
"wasi", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "itoa" |
||||||
|
version = "0.4.7" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "lazy_static" |
||||||
|
version = "1.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "libc" |
||||||
|
version = "0.2.85" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "log" |
||||||
|
version = "0.4.14" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" |
||||||
|
dependencies = [ |
||||||
|
"cfg-if", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "proc-macro2" |
||||||
|
version = "1.0.24" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" |
||||||
|
dependencies = [ |
||||||
|
"unicode-xid", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "quote" |
||||||
|
version = "1.0.8" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "redox_syscall" |
||||||
|
version = "0.1.57" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ryu" |
||||||
|
version = "1.0.5" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "serde" |
||||||
|
version = "1.0.123" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" |
||||||
|
dependencies = [ |
||||||
|
"serde_derive", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "serde_derive" |
||||||
|
version = "1.0.123" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"syn", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "serde_json" |
||||||
|
version = "1.0.61" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" |
||||||
|
dependencies = [ |
||||||
|
"itoa", |
||||||
|
"ryu", |
||||||
|
"serde", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "simple-logging" |
||||||
|
version = "2.0.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b00d48e85675326bb182a2286ea7c1a0b264333ae10f27a937a72be08628b542" |
||||||
|
dependencies = [ |
||||||
|
"lazy_static", |
||||||
|
"log", |
||||||
|
"thread-id", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "syn" |
||||||
|
version = "1.0.60" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"unicode-xid", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "thiserror" |
||||||
|
version = "1.0.23" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" |
||||||
|
dependencies = [ |
||||||
|
"thiserror-impl", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "thiserror-impl" |
||||||
|
version = "1.0.23" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"syn", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "thread-id" |
||||||
|
version = "3.3.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" |
||||||
|
dependencies = [ |
||||||
|
"libc", |
||||||
|
"redox_syscall", |
||||||
|
"winapi", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "unicode-xid" |
||||||
|
version = "0.2.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "uuid" |
||||||
|
version = "0.8.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" |
||||||
|
dependencies = [ |
||||||
|
"getrandom", |
||||||
|
"serde", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "wasi" |
||||||
|
version = "0.10.2+wasi-snapshot-preview1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "winapi" |
||||||
|
version = "0.3.9" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" |
||||||
|
dependencies = [ |
||||||
|
"winapi-i686-pc-windows-gnu", |
||||||
|
"winapi-x86_64-pc-windows-gnu", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "winapi-i686-pc-windows-gnu" |
||||||
|
version = "0.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "winapi-x86_64-pc-windows-gnu" |
||||||
|
version = "0.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "yopa" |
||||||
|
version = "0.1.0" |
||||||
|
dependencies = [ |
||||||
|
"anyhow", |
||||||
|
"log", |
||||||
|
"serde", |
||||||
|
"serde_json", |
||||||
|
"simple-logging", |
||||||
|
"thiserror", |
||||||
|
"uuid", |
||||||
|
] |
@ -0,0 +1,19 @@ |
|||||||
|
[package] |
||||||
|
name = "yopa" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
log = "0.4.13" |
||||||
|
simple-logging = "2.0.2" |
||||||
|
|
||||||
|
uuid = { version = "0.8", features = ["serde", "v4"] } |
||||||
|
serde_json = "1.0.61" |
||||||
|
serde = { version = "1.0.120", features = ["derive"] } |
||||||
|
|
||||||
|
#parking_lot = "0.11.1" |
||||||
|
anyhow = "1.0.38" |
||||||
|
thiserror = "1.0.23" |
@ -0,0 +1,78 @@ |
|||||||
|
#[macro_use] extern crate log; |
||||||
|
|
||||||
|
use log::LevelFilter; |
||||||
|
|
||||||
|
mod yopa; |
||||||
|
use yopa::model; |
||||||
|
use yopa::model::DataType; |
||||||
|
use crate::yopa::model::TypedValue; |
||||||
|
use anyhow::Error; |
||||||
|
|
||||||
|
fn main() { |
||||||
|
simple_logging::log_to_stderr(LevelFilter::Debug); |
||||||
|
|
||||||
|
if let Err(e) = _main() { |
||||||
|
error!("{:?}", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn _main() -> anyhow::Result<()> { |
||||||
|
simple_logging::log_to_stderr(LevelFilter::Debug); |
||||||
|
|
||||||
|
let mut store = yopa::InMemoryStorage::new(); |
||||||
|
|
||||||
|
let recipe_tid = store.insert_object_template(model::ObjectTemplate { |
||||||
|
id: Default::default(), |
||||||
|
name: "recipe".to_string(), |
||||||
|
parent_tpl_id: None |
||||||
|
})?; |
||||||
|
|
||||||
|
store.insert_property_template(model::PropertyTemplate { |
||||||
|
id: Default::default(), |
||||||
|
parent_tpl_id: recipe_tid, |
||||||
|
name: "title".to_string(), |
||||||
|
optional: false, |
||||||
|
multiple: false, |
||||||
|
data_type: DataType::String, |
||||||
|
default: None |
||||||
|
})?; |
||||||
|
|
||||||
|
store.insert_property_template(model::PropertyTemplate { |
||||||
|
id: Default::default(), |
||||||
|
parent_tpl_id: recipe_tid, |
||||||
|
name: "prep_hours".to_string(), |
||||||
|
optional: true, |
||||||
|
multiple: false, |
||||||
|
data_type: DataType::Decimal, |
||||||
|
default: None |
||||||
|
})?; |
||||||
|
|
||||||
|
let book_tid = store.insert_object_template(model::ObjectTemplate { |
||||||
|
id: Default::default(), |
||||||
|
name: "book".to_string(), |
||||||
|
parent_tpl_id: None |
||||||
|
})?; |
||||||
|
|
||||||
|
let br_rid = store.insert_relation_template(model::RelationTemplate { |
||||||
|
id: Default::default(), |
||||||
|
object_tpl_id: recipe_tid, |
||||||
|
name: "book reference".to_string(), |
||||||
|
optional: false, |
||||||
|
multiple: false, |
||||||
|
related_tpl_id: book_tid |
||||||
|
})?; |
||||||
|
|
||||||
|
store.insert_property_template(model::PropertyTemplate { |
||||||
|
id: Default::default(), |
||||||
|
parent_tpl_id: br_rid, |
||||||
|
name: "page".to_string(), |
||||||
|
optional: true, |
||||||
|
multiple: false, |
||||||
|
data_type: DataType::Integer, |
||||||
|
default: None |
||||||
|
})?; |
||||||
|
|
||||||
|
debug!("{:#?}", store); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,299 @@ |
|||||||
|
use std::collections::{HashMap}; |
||||||
|
use std::iter::Extend; |
||||||
|
use crate::yopa::model::{ObjectTemplate, ID}; |
||||||
|
use thiserror::Error; |
||||||
|
use std::hash::Hash; |
||||||
|
|
||||||
|
/// Data model structs and enums
|
||||||
|
pub mod model { |
||||||
|
use serde::{Serialize, Deserialize}; |
||||||
|
use std::borrow::Cow; |
||||||
|
|
||||||
|
/// Common identifier type
|
||||||
|
#[allow(non_camel_case_types)] |
||||||
|
pub type ID = uuid::Uuid; |
||||||
|
|
||||||
|
/// Object template
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)] |
||||||
|
pub struct ObjectTemplate { |
||||||
|
/// PK
|
||||||
|
pub id : ID, |
||||||
|
/// Template name, unique within the database
|
||||||
|
pub name: String, |
||||||
|
/// Parent object template ID
|
||||||
|
pub parent_tpl_id: Option<ID>, |
||||||
|
} |
||||||
|
|
||||||
|
/// Relation between templates
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)] |
||||||
|
pub struct RelationTemplate { |
||||||
|
/// 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 PropertyTemplate { |
||||||
|
/// 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<TypedValue>, |
||||||
|
} |
||||||
|
|
||||||
|
/// Value data type
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)] |
||||||
|
pub enum DataType { |
||||||
|
/// Text
|
||||||
|
String, |
||||||
|
/// Integer
|
||||||
|
Integer, |
||||||
|
/// Floating point number
|
||||||
|
Decimal, |
||||||
|
/// Boolean yes/no
|
||||||
|
Boolean, |
||||||
|
} |
||||||
|
|
||||||
|
/// Value of a particular type
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)] |
||||||
|
pub enum TypedValue { |
||||||
|
/// Text
|
||||||
|
String(Cow<'static, str>), |
||||||
|
/// Integer
|
||||||
|
Integer(i64), |
||||||
|
/// Floating point number
|
||||||
|
Decimal(f64), |
||||||
|
/// Boolean yes/no
|
||||||
|
Boolean(bool), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Data value structs
|
||||||
|
pub mod data { |
||||||
|
use serde::{Serialize, Deserialize}; |
||||||
|
use crate::yopa::model::{ID, TypedValue}; |
||||||
|
|
||||||
|
/// Instance of an object
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)] |
||||||
|
pub struct Object { |
||||||
|
/// PK
|
||||||
|
pub id : ID, |
||||||
|
/// Object template ID
|
||||||
|
pub object_tpl_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 rel_tpl_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 prop_tpl_id: ID, |
||||||
|
/// Property value
|
||||||
|
pub value : TypedValue, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Default)] |
||||||
|
pub struct InMemoryStorage { |
||||||
|
tpl_objects: HashMap<ID, model::ObjectTemplate>, |
||||||
|
tpl_relations: HashMap<ID, model::RelationTemplate>, |
||||||
|
tpl_properties: HashMap<ID, model::PropertyTemplate>, |
||||||
|
|
||||||
|
data_objects: HashMap<ID, data::Object>, |
||||||
|
data_relations: HashMap<ID, data::Relation>, |
||||||
|
data_properties: HashMap<ID, data::Value>, |
||||||
|
} |
||||||
|
|
||||||
|
fn next_id() -> ID { |
||||||
|
uuid::Uuid::new_v4() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn zero_id() -> ID { |
||||||
|
uuid::Uuid::nil() |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug,Error)] |
||||||
|
pub enum StorageError { |
||||||
|
#[error("Referenced {0} does not exist")] |
||||||
|
NotExist(&'static str), |
||||||
|
#[error("Schema constraint violation: {0}")] |
||||||
|
ConstraintViolation(&'static str), |
||||||
|
} |
||||||
|
|
||||||
|
impl InMemoryStorage { |
||||||
|
pub fn new() -> Self { |
||||||
|
Self::default() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn insert_object_template(&mut self, mut tpl : model::ObjectTemplate) -> Result<ID, StorageError> { |
||||||
|
if let Some(pid) = tpl.parent_tpl_id { |
||||||
|
if !self.tpl_objects.contains_key(&pid) { |
||||||
|
return Err(StorageError::NotExist("parent object template")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if self.tpl_objects.iter().find(|(_, t)| t.name == tpl.name).is_some() { |
||||||
|
return Err(StorageError::ConstraintViolation("object template with this name already exists")); |
||||||
|
} |
||||||
|
|
||||||
|
let id = next_id(); |
||||||
|
tpl.id = id; |
||||||
|
self.tpl_objects.insert(id, tpl); |
||||||
|
Ok(id) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn insert_relation_template(&mut self, mut rel: model::RelationTemplate) -> Result<ID, StorageError> { |
||||||
|
if !self.tpl_objects.contains_key(&rel.object_tpl_id) { |
||||||
|
return Err(StorageError::NotExist("origin object template")); |
||||||
|
} |
||||||
|
if !self.tpl_objects.contains_key(&rel.related_tpl_id) { |
||||||
|
return Err(StorageError::NotExist("related object template")); |
||||||
|
} |
||||||
|
|
||||||
|
if self.tpl_relations.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")); |
||||||
|
} |
||||||
|
|
||||||
|
let id = next_id(); |
||||||
|
rel.id = id; |
||||||
|
self.tpl_relations.insert(id, rel); |
||||||
|
Ok(id) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn insert_property_template(&mut self, mut prop: model::PropertyTemplate) -> Result<ID, StorageError> { |
||||||
|
if !self.tpl_objects.contains_key(&prop.parent_tpl_id) { |
||||||
|
// Maybe it's attached to a relation?
|
||||||
|
if !self.tpl_relations.contains_key(&prop.parent_tpl_id) { |
||||||
|
return Err(StorageError::NotExist("object or reference template")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if self.tpl_properties.iter().find(|(_, t)| t.parent_tpl_id == prop.parent_tpl_id && t.name == prop.name).is_some() { |
||||||
|
return Err(StorageError::ConstraintViolation("property with this name and parent already exists")); |
||||||
|
} |
||||||
|
|
||||||
|
let id = next_id(); |
||||||
|
prop.id = id; |
||||||
|
self.tpl_properties.insert(id, prop); |
||||||
|
Ok(id) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn delete_object_template(&mut self, id : ID) -> Result<ObjectTemplate, StorageError> { |
||||||
|
return if let Some(t) = self.tpl_objects.remove(&id) { |
||||||
|
// Remove relation templates
|
||||||
|
let removed_relation_ids = map_drain_filter(&mut self.tpl_relations, |_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.tpl_properties, |_k, v| v.parent_tpl_id == id || removed_relation_ids.contains(&v.parent_tpl_id)) |
||||||
|
.keys(); |
||||||
|
|
||||||
|
// Remove objects
|
||||||
|
let _ = map_drain_filter(&mut self.data_objects, |_k, v| v.object_tpl_id == id); |
||||||
|
|
||||||
|
// Remove property values
|
||||||
|
let _ = map_drain_filter(&mut self.data_properties, |_k, v| removed_prop_ids.contains(&v.prop_tpl_id)); |
||||||
|
|
||||||
|
// Remove relations
|
||||||
|
let _ = map_drain_filter(&mut self.data_relations, |_k, v| removed_relation_ids.contains(&v.rel_tpl_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("object template")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn delete_relation_template(&mut self, id : ID) -> Result<model::RelationTemplate, StorageError> { |
||||||
|
return if let Some(t) = self.tpl_relations.remove(&id) { |
||||||
|
// Remove relations
|
||||||
|
let _ = map_drain_filter(&mut self.data_relations, |_k, v| v.rel_tpl_id == id).keys(); |
||||||
|
|
||||||
|
// Remove related property templates
|
||||||
|
let removed_prop_tpl_ids = map_drain_filter(&mut self.tpl_properties, |_k, v| v.parent_tpl_id == id).keys(); |
||||||
|
|
||||||
|
let _ = map_drain_filter(&mut self.data_properties, |_k, v| removed_prop_tpl_ids.contains(&v.prop_tpl_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("relation template")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn delete_property_template(&mut self, id : ID) -> Result<model::PropertyTemplate, StorageError> { |
||||||
|
return if let Some(t) = self.tpl_properties.remove(&id) { |
||||||
|
// Remove relations
|
||||||
|
let _ = map_drain_filter(&mut self.data_properties, |_k, v| v.prop_tpl_id == id); |
||||||
|
Ok(t) |
||||||
|
} else { |
||||||
|
Err(StorageError::NotExist("property template")) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn map_drain_filter<K : Eq + Hash, V>(map : &mut HashMap<K, V>, filter : impl Fn(&K, &V) -> bool) -> Vec<(K, V)> { |
||||||
|
let mut removed = vec![]; |
||||||
|
let mut retain = vec![]; |
||||||
|
for (k, v) in map.drain() { |
||||||
|
if filter(&k, &v) { |
||||||
|
removed.push((k, v)); |
||||||
|
} else { |
||||||
|
retain.push((k, v)); |
||||||
|
} |
||||||
|
} |
||||||
|
map.extend(retain); |
||||||
|
removed |
||||||
|
} |
||||||
|
|
||||||
|
trait KVVecToKeysOrValues<K, V> { |
||||||
|
fn keys(self) -> Vec<K>; |
||||||
|
fn values(self) -> Vec<V>; |
||||||
|
} |
||||||
|
|
||||||
|
impl<K, V> KVVecToKeysOrValues<K, V> for Vec<(K,V)> { |
||||||
|
fn keys(self) -> Vec<K> { |
||||||
|
self.into_iter().map(|(k, _v)| k).collect() |
||||||
|
} |
||||||
|
|
||||||
|
fn values(self) -> Vec<V> { |
||||||
|
self.into_iter().map(|(_k, v)| v).collect() |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue