finish implementing the modular object format #3

Open
sel wants to merge 8 commits from connections into main
13 changed files with 442 additions and 75 deletions

View file

@ -27,7 +27,7 @@ object templates can define several options for each property:
- `transforms`: uses this property as an input of a new object - `transforms`: uses this property as an input of a new object
- `subobjects`: creates a parent-child relationship between this and at least one more object, based on the properties sharing a value and their transformations - `subobjects`: creates a parent-child relationship between this and at least one more object, based on the properties sharing a value and their transformations
- `conditions`: values other properties need to hold for this property to be valid - `conditions`: values other properties need to hold for this property to be valid
- `duplicates`: makes the property act as an array - `array`: makes the property act as an array
for more information, see the wiki! for more information, see the wiki!

91
server/Cargo.lock generated
View file

@ -2,18 +2,78 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.0" version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.12.0" version = "2.12.0"
@ -30,6 +90,12 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "libc"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.6" version = "2.7.6"
@ -112,6 +178,17 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.110" version = "2.0.110"
@ -162,18 +239,32 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.22" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "wgu" name = "wgu"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"hex",
"serde", "serde",
"serde_json", "serde_json",
"sha2",
"toml", "toml",
] ]

View file

@ -5,6 +5,8 @@ version = "0.0.1"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
hex = "0.4.3"
serde = { version = "*", features = ["derive"] } serde = { version = "*", features = ["derive"] }
serde_json = "1.0.145" serde_json = "1.0.145"
sha2 = "0.10.9"
toml = "0.9.8" toml = "0.9.8"

View file

@ -1,20 +1,83 @@
pub mod modules; pub mod modules;
pub mod types; pub mod types;
use crate::modules::{ModuleItem, item_registry};
use crate::types::{ObjectInstance, ObjectTemplate}; use std::time::{SystemTime, UNIX_EPOCH};
use std::collections::HashMap;
use crate::modules::{ModuleItem, FunctionItem, item_registry};
use crate::types::{FunctionTemplate, FunctionTemplateObject, ObjectInstance, ObjectTemplate};
use serde_json::{Value, json}; use serde_json::{Value, json};
use sha2::{Sha256, Digest};
use hex;
impl ObjectTemplate {
fn from_object_type(object_type: &str) -> Result<ObjectTemplate, String> {
let modules = item_registry();
let template: ObjectTemplate = match modules.get(object_type) {
Some(ModuleItem::Template(template_str)) => toml::from_str(template_str).unwrap(),
Some(_) => panic!("{} is not a ModuleItem::Template", object_type),
None => return Err(format!("ModuleItem {} doesn't exist", object_type))
};
Ok(template)
}
}
impl ObjectInstance { impl ObjectInstance {
pub fn from_object_type(object_type: &str, input: Value) -> Result<Self, String> {
let mut instance = ObjectInstance {
object_type: object_type.to_string(),
input: json!(input),
..ObjectInstance::default()
};
// https://stackoverflow.com/questions/26593387/how-can-i-get-the-current-time-in-milliseconds
instance.created = format!("{:?}", SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time should go forward")
.as_millis());
instance.edited = format!("{:?}", SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time should go forward")
.as_millis());
instance.hashed = format!("{:?}", SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time should go forward")
.as_millis());
let input_sha256_bytes = Sha256::digest(
format!("{}+{}",
instance.object_type,
json!(instance.input)
)
);
instance.input_sha256 = hex::encode(input_sha256_bytes);
let full_sha256_bytes = Sha256::digest(
format!("{}+{}+{}+{}+{}",
instance.object_type,
instance.hashed,
json!(instance.input),
json!(instance.local),
json!(instance.remote)
)
);
instance.full_sha256 = hex::encode(full_sha256_bytes);
ObjectInstance::validate(&instance)?;
Ok(instance)
}
pub fn validate(obj: &ObjectInstance) -> Result<(), String> { pub fn validate(obj: &ObjectInstance) -> Result<(), String> {
let modules = item_registry(); let modules = item_registry();
let object_type = &obj.object_type; let object_type = &obj.object_type;
let template = ObjectTemplate::from_object_type(object_type)?;
let template: ObjectTemplate = match modules.get(object_type.as_str()) {
Some(ModuleItem::Template(template_str)) => toml::from_str(template_str).unwrap(),
Some(_) => panic!("{} is not a ModuleItem::Template", object_type),
None => return Err(format!("template {} doesn't exist", object_type))
};
match modules.get(format!("{}:func:validator", object_type).as_str()) { match modules.get(format!("{}:func:validator", object_type).as_str()) {
Some(ModuleItem::Validator(validate)) => validate(&obj)?, Some(ModuleItem::Validator(validate)) => validate(&obj)?,
@ -24,7 +87,7 @@ impl ObjectInstance {
for (name, property) in template.input { for (name, property) in template.input {
for transform in property.transforms { for transform in property.transforms {
let destination = transform.split(":").collect::<Vec<&str>>(); let destination: Vec<&str> = transform.splitn(2, ":").collect();
let temp_object = ObjectInstance { let temp_object = ObjectInstance {
object_type: destination[0].to_string(), object_type: destination[0].to_string(),
@ -45,14 +108,115 @@ impl ObjectInstance {
Ok(()) Ok(())
} }
pub fn from_template(object_type: &str, input: Value) -> Result<Self, String> { pub fn run_transform(
let instance = ObjectInstance { obj: &ObjectInstance,
object_type: object_type.to_string(), source: &str,
input: json!(input), destination: &str
..ObjectInstance::default() )
-> Result<Self, String>
{
let object_type = &obj.object_type;
let template = ObjectTemplate::from_object_type(object_type)?;
// assuming that the property to transform (source) is 'input.some.thing.nya',
// source_parts[0] is the category (input)
// source_parts[1] is the name (some.thing.nya)
let source_parts: Vec<&str> = source.splitn(2, ".").collect();
// assuming that the transform destination is 'meta/text:value',
// destination_parts[0] is the object type (meta/text)
// destination_parts[1] is the property under `input` (value)
let destination_parts: Vec<&str> = destination.splitn(2, ":").collect();
let source_instance_category = match source_parts[0] {
"input" => &obj.input,
"local" => &obj.local,
"remote" => &obj.remote,
_ => return Err(format!("the category specified in the transformation source ({}) is invalid (must be input OR local OR remote)", source_parts[0]))
}; };
ObjectInstance::validate(&instance)?; let source_template_category = match source_parts[0] {
Ok(instance) "input" => template.input,
"local" => template.local,
"remote" => template.remote,
_ => unreachable!()
};
let template_property = match source_template_category.get(source_parts[1]) {
Some(template_property) => template_property,
None => return Err(format!("the property specified as the transformation source ({}) doesn't exist in the template", source))
};
if template_property.transforms.contains(&destination.to_string()) {
let result = ObjectInstance::from_object_type(destination_parts[0], json!({
destination_parts[1]: source_instance_category.get(source_parts[1])
}))?;
Ok(result)
} else {
Err(format!("invalid destination {}", destination))
}
}
pub fn run_function(
function: &str,
inputs: FunctionItem,
params: Option<FunctionItem>
)
-> Result<FunctionItem, String>
{
let modules = item_registry();
// same thing as in ObjectTemplate::from_object_type
let (template, function): (FunctionTemplate, _) = match modules.get(function) {
Some(ModuleItem::Function((template_str, function))) => (toml::from_str(template_str).unwrap(), function),
Some(_) => return Err(format!("the ModuleItem `{}` is not a ModuleItem::Function", function)),
None => return Err(format!("the ModuleItem `{}` doesn't exist", function))
};
validate_function_data(&inputs, &template.inputs, "input")?;
if let Some(params) = &params {
validate_function_data(params, &template.params, "parameter")?;
}
let result = function(inputs.clone(), params.clone())?;
validate_function_data(&result, &template.outputs, "output")?;
Ok(result)
} }
} }
// helpers
fn validate_function_data(data: &FunctionItem, template: &HashMap<String, FunctionTemplateObject>, category: &str) -> Result<(), String> {
for (name, template_object) in template {
if let None = data.get(name) && template_object.required {
return Err(format!("required {} {} doesn't exist", category, name))
}
}
for (name, instances) in data {
let template_object = match template.get(name) {
Some(template_object) => template_object,
None => return Err(format!("{} {} doesn't exist in the template", "input", name))
};
let expected_count = &template_object.count;
let expected_object_type = &template_object.object_type;
if instances.len() != *expected_count {
return Err(format!("the amount of object instances in the {} {} ({}) doesn't match the expected amount ({})", category, name, instances.len(), expected_count));
}
for instance in instances {
if instance.object_type != *expected_object_type {
return Err(format!("the object type of one or more object instances in the {} {} ({}) doesn't match the expected type ({})", category, name, instance.object_type, expected_object_type))
}
ObjectInstance::validate(&instance)?;
}
}
Ok(())
}

View file

@ -1,35 +1,33 @@
use std::collections::HashMap;
use serde_json::json; use serde_json::json;
use wgu::types::ObjectInstance; use wgu::types::ObjectInstance;
use wgu::modules::{ModuleItem, item_registry};
fn main() -> Result<(), String> { fn main() -> Result<(), String> {
let modules = item_registry();
// change these! // change these!
// 1 | creating a new object instance
let object_type = "meta/number"; let object_type = "meta/number";
let input = json!({ let input = json!({
"value": 1312 "value": 1312
}); });
let function = "local";
// this creates a new object instance let some_object = ObjectInstance::from_object_type(object_type, input)?;
let some_object = ObjectInstance::from_template(object_type, input)?;
println!("this is an object:"); println!("this is an object:");
println!("{:?}", some_object); println!("{:#?}", some_object);
println!(""); println!("");
// this runs a function on it to calculate other properties // 2 | running a function on it
let some_object_with_data = match modules.get(format!("{}:func:{}", object_type, function).as_str()) { let function = "meta/number:func:local";
Some(ModuleItem::Calculator(calc)) => calc(&some_object)?, let mut function_input = HashMap::new();
Some(_) => return Err(format!("if you're trying to run a ModuleItem::Function and not a ModuleItem::Calculator, you'll have to add the match arm for that")), function_input.insert(format!("main"), vec![some_object.clone()]);
None => {
return Err(format!("the ModuleItem `{}:func:{}` doesn't exist. this is not necessarily bad in this example (if you changed `object_type`)", object_type, function));
}
};
println!("this is the same object with data:"); println!("running function {}", function);
println!("{:?}", some_object_with_data); println!("input: {:#?}", function_input);
let some_object_with_data = ObjectInstance::run_function(function, function_input, None)?;
println!("output: {:#?}", some_object_with_data);
Ok(()) Ok(())
} }

View file

@ -3,14 +3,15 @@ use std::collections::HashMap;
pub mod meta; pub mod meta;
pub type FunctionItem = HashMap<String, Vec<ObjectInstance>>;
pub enum ModuleItem { pub enum ModuleItem {
Template(&'static str), Template(&'static str),
Validator(fn(&ObjectInstance) -> Result<(), String>), Validator(fn(&ObjectInstance) -> Result<(), String>),
Calculator(fn(&ObjectInstance) -> Result<ObjectInstance, String>), Function((
Function(fn( &'static str, // template
inputs: HashMap<&str, &ObjectInstance>, fn(inputs: FunctionItem, params: Option<FunctionItem>) -> Result<FunctionItem, String>
params: Option<HashMap<&str, &ObjectInstance>> ))
) -> Result<HashMap<String, ObjectInstance>, String>)
} }
pub fn item_registry() -> HashMap<&'static str, ModuleItem> { pub fn item_registry() -> HashMap<&'static str, ModuleItem> {

View file

@ -5,6 +5,7 @@ mod dummy;
mod text; mod text;
mod number; mod number;
mod bool; mod bool;
mod connection;
pub fn registry() -> HashMap<&'static str, ModuleItem> { pub fn registry() -> HashMap<&'static str, ModuleItem> {
let mut map = HashMap::new(); let mut map = HashMap::new();
@ -13,15 +14,18 @@ pub fn registry() -> HashMap<&'static str, ModuleItem> {
// meta/text // meta/text
map.insert("meta/text", ModuleItem::Template(text::TEMPLATE)); map.insert("meta/text", ModuleItem::Template(text::TEMPLATE));
map.insert("meta/text:func:validator", ModuleItem::Validator(text::validate)); map.insert("meta/text:func:validator", ModuleItem::Validator(text::validate));
map.insert("meta/text:func:local", ModuleItem::Calculator(text::local)); map.insert("meta/text:func:local", ModuleItem::Function((text::TEMPLATE_LOCAL, text::local)));
// meta/number // meta/number
map.insert("meta/number", ModuleItem::Template(number::TEMPLATE)); map.insert("meta/number", ModuleItem::Template(number::TEMPLATE));
map.insert("meta/number:func:validator", ModuleItem::Validator(number::validate)); map.insert("meta/number:func:validator", ModuleItem::Validator(number::validate));
map.insert("meta/number:func:local", ModuleItem::Calculator(number::local)); map.insert("meta/number:func:local", ModuleItem::Function((number::TEMPLATE_LOCAL, number::local)));
// meta/bool // meta/bool
map.insert("meta/bool", ModuleItem::Template(bool::TEMPLATE)); map.insert("meta/bool", ModuleItem::Template(bool::TEMPLATE));
map.insert("meta/bool:func:validator", ModuleItem::Validator(bool::validate)); map.insert("meta/bool:func:validator", ModuleItem::Validator(bool::validate));
// meta/connection
map.insert("meta/connection", ModuleItem::Template(connection::TEMPLATE));
map map
} }

View file

@ -5,7 +5,7 @@ pub const TEMPLATE: &str = r#"
transforms = [] transforms = []
subobjects = [] subobjects = []
conditions = [] conditions = []
duplicates = false array = false
"#; "#;
pub fn validate(obj: &ObjectInstance) -> Result<(), String> { pub fn validate(obj: &ObjectInstance) -> Result<(), String> {

View file

@ -0,0 +1,41 @@
pub const TEMPLATE: &str = r#"
[input.variant]
transforms = ["meta/text:value"]
subobjects = []
conditions = [
["input.variant", "function"]
["input.variant", "transform"]
["input.variant", "subobject"]
["input.variant", "custom"]
]
array = false
[input.from]
transforms = ["meta/text:value"]
subobjects = []
conditions = []
array = true
[input.to]
transforms = ["meta/text:value"]
subobjects = []
conditions = []
array = true
[input.property]
transforms = ["meta/text:value"]
subobjects = []
conditions = [
["input.variant", "transform"]
["input.variant", "subobject"]
]
array = false
[input.value]
transforms = ["meta/text:value"]
subobjects = []
conditions = [
["input.variant", "custom"]
]
array = false
"#;

View file

@ -1,6 +1,7 @@
pub const TEMPLATE: &str = r#"[input.value] pub const TEMPLATE: &str = r#"
[input.value]
transforms = ["meta/text:value"] transforms = ["meta/text:value"]
subobjects = [] subobjects = []
conditions = [] conditions = []
duplicates = false array = false
"#; "#;

View file

@ -1,18 +1,32 @@
use std::collections::HashMap;
use serde_json::json; use serde_json::json;
use crate::ObjectInstance;
use crate::{modules::FunctionItem, types::ObjectInstance};
pub const TEMPLATE: &str = r#" pub const TEMPLATE: &str = r#"
[input.value] [input.value]
transforms = [] transforms = []
subobjects = [] subobjects = []
conditions = [] conditions = []
duplicates = false array = false
[local.length] [local.length]
transforms = ["meta/number:value"] transforms = ["meta/number:value"]
subobjects = [] subobjects = []
conditions = [] conditions = []
duplicates = false array = false
"#;
pub const TEMPLATE_LOCAL: &str = r#"
[inputs.main]
required = true
object_type = "meta/number"
count = 1
[outputs.main]
required = true
object_type = "meta/number"
count = 1
"#; "#;
pub fn validate(obj: &ObjectInstance) -> Result<(), String> { pub fn validate(obj: &ObjectInstance) -> Result<(), String> {
@ -25,18 +39,28 @@ pub fn validate(obj: &ObjectInstance) -> Result<(), String> {
Ok(()) Ok(())
} }
pub fn local(obj: &ObjectInstance) -> Result<ObjectInstance, String> { pub fn local(
let _value = obj.input.get("value") inputs: FunctionItem,
.ok_or("input.value must exist")?; _params: Option<FunctionItem>
)
-> Result<FunctionItem, String>
{
let Some(main) = inputs.get("main")
else { unreachable!() };
let value = _value.as_number() let main_obj = &main[0];
.ok_or("input.value must be a number")?;
let mut new = obj.clone(); let Some(value) = &main_obj.input.get("value")
new.local = json!({ else { unreachable!() };
let mut result_obj = ObjectInstance::from_object_type("meta/number", main_obj.input.clone())?;
result_obj.local = json!({
"length": value.to_string().len() "length": value.to_string().len()
}); });
Ok(new) let mut result = HashMap::new();
} result.insert(format!("main"), vec![result_obj]);
Ok(result)
}

View file

@ -1,18 +1,33 @@
use std::collections::HashMap;
use serde_json::json; use serde_json::json;
use crate::ObjectInstance;
use crate::{modules::FunctionItem, types::ObjectInstance};
pub const TEMPLATE: &str = r#" pub const TEMPLATE: &str = r#"
[input.value] [input.value]
transforms = [] transforms = []
subobjects = [] subobjects = []
conditions = [] conditions = []
duplicates = false array = false
[local.length] [local.length]
transforms = ["meta/number:value"] transforms = ["meta/number:value"]
subobjects = [] subobjects = []
conditions = [] conditions = []
duplicates = false array = false
"#;
pub const TEMPLATE_LOCAL: &str = r#"
[inputs.main]
required = true
object_type = "meta/text"
count = 1
[outputs.main]
required = true
object_type = "meta/text"
count = 1
"#; "#;
pub fn validate(obj: &ObjectInstance) -> Result<(), String> { pub fn validate(obj: &ObjectInstance) -> Result<(), String> {
@ -25,17 +40,28 @@ pub fn validate(obj: &ObjectInstance) -> Result<(), String> {
Ok(()) Ok(())
} }
pub fn local(obj: &ObjectInstance) -> Result<ObjectInstance, String> { pub fn local(
let value = obj.input.get("value") inputs: FunctionItem,
.ok_or("input.value must exist")?; _params: Option<FunctionItem>
)
-> Result<FunctionItem, String>
{
let Some(main) = inputs.get("main")
else { unreachable!() };
let value = value.as_str() let main_obj = &main[0];
.ok_or("input.value must be a string")?;
let mut new = obj.clone(); let Some(value) = &main_obj.input.get("value")
new.local = json!({ else { unreachable!() };
"length": value.len()
let mut result_obj = ObjectInstance::from_object_type("meta/text", main_obj.input.clone())?;
result_obj.local = json!({
"length": value.to_string().len()
}); });
Ok(new) let mut result = HashMap::new();
result.insert(format!("main"), vec![result_obj]);
Ok(result)
} }

View file

@ -4,7 +4,7 @@ use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug, Default, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct ObjectInstance { pub struct ObjectInstance {
pub variant: u32, pub version: usize,
pub plotted: bool, pub plotted: bool,
pub created: String, pub created: String,
pub edited: String, pub edited: String,
@ -24,24 +24,39 @@ pub struct Log {
level: String, level: String,
variant: String, variant: String,
timestamp: String, timestamp: String,
message: String, message: String
} }
#[derive(Deserialize, Debug, Default)] #[derive(Deserialize, Debug, Default)]
#[serde(default)] #[serde(default)]
pub struct ObjectTemplate { pub struct ObjectTemplate {
pub input: HashMap<String, TemplateProperty>, pub input: HashMap<String, ObjectTemplateProperty>,
pub local: HashMap<String, TemplateProperty>, pub local: HashMap<String, ObjectTemplateProperty>,
pub remote: HashMap<String, TemplateProperty>, pub remote: HashMap<String, ObjectTemplateProperty>
} }
// part of ObjectTemplate // part of ObjectTemplate
#[derive(Deserialize, Debug, Default)] #[derive(Deserialize, Debug, Default)]
#[serde(default)] #[serde(default)]
pub struct TemplateProperty { pub struct ObjectTemplateProperty {
pub transforms: Vec<String>, pub transforms: Vec<String>,
pub subobjects: Vec<String>, pub subobjects: Vec<String>,
pub conditions: Vec<[String; 2]>, pub conditions: Vec<[String; 2]>,
pub duplicates: bool pub array: bool
} }
#[derive(Deserialize, Debug, Default)]
#[serde(default)]
pub struct FunctionTemplate {
pub inputs: HashMap<String, FunctionTemplateObject>,
pub params: HashMap<String, FunctionTemplateObject>,
pub outputs: HashMap<String, FunctionTemplateObject>
}
#[derive(Deserialize, Debug, Default)]
#[serde(default)]
pub struct FunctionTemplateObject {
pub required: bool,
pub object_type: String,
pub count: usize
}