From cedea2f305503baa385a6bd7a130ae4a00d35e83 Mon Sep 17 00:00:00 2001 From: Timur Gordon <31495328+timurgordon@users.noreply.github.com> Date: Thu, 21 Aug 2025 14:05:01 +0200 Subject: [PATCH] move rhai wrappers of models from rhailib --- Cargo.lock | 274 ++++++++++- heromodels/Cargo.toml | 2 + heromodels/examples/legal_contract_example.rs | 2 +- heromodels/src/models/access/rhai.rs | 148 ++++++ heromodels/src/models/biz/rhai.rs | 422 +++++++++++++++++ heromodels/src/models/calendar/rhai.rs | 246 ++++++++++ heromodels/src/models/circle/rhai.rs | 441 ++++-------------- heromodels/src/models/contact/rhai.rs | 232 +++++++++ heromodels/src/models/core/rhai.rs | 86 ++++ heromodels/src/models/finance/rhai.rs | 80 ++++ heromodels/src/models/grid4/mod.rs | 16 + heromodels/src/models/grid4/node.rs | 265 +++++++++++ heromodels/src/models/heroledger/rhai.rs | 41 +- heromodels/src/models/library/rhai.rs | 156 +++++++ heromodels/src/models/location/address.rs | 11 + heromodels/src/models/location/mod.rs | 2 + heromodels/src/models/mod.rs | 3 + heromodels/src/models/object/rhai.rs | 27 ++ heromodels/src/models/payment/rhai.rs | 49 ++ 19 files changed, 2110 insertions(+), 393 deletions(-) create mode 100644 heromodels/src/models/access/rhai.rs create mode 100644 heromodels/src/models/biz/rhai.rs create mode 100644 heromodels/src/models/calendar/rhai.rs create mode 100644 heromodels/src/models/contact/rhai.rs create mode 100644 heromodels/src/models/core/rhai.rs create mode 100644 heromodels/src/models/finance/rhai.rs create mode 100644 heromodels/src/models/grid4/mod.rs create mode 100644 heromodels/src/models/grid4/node.rs create mode 100644 heromodels/src/models/library/rhai.rs create mode 100644 heromodels/src/models/location/address.rs create mode 100644 heromodels/src/models/location/mod.rs create mode 100644 heromodels/src/models/object/rhai.rs create mode 100644 heromodels/src/models/payment/rhai.rs diff --git a/Cargo.lock b/Cargo.lock index 6409f95..d6977f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -60,7 +71,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -116,6 +127,18 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -125,12 +148,57 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -158,6 +226,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.41" @@ -268,6 +342,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures-channel" version = "0.3.31" @@ -292,7 +372,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -361,6 +441,15 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.15.4" @@ -387,6 +476,8 @@ dependencies = [ "r2d2", "r2d2_postgres", "rhai", + "rhailib-macros", + "rust_decimal", "serde", "serde_json", "strum", @@ -403,7 +494,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.104", ] [[package]] @@ -454,7 +545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.4", ] [[package]] @@ -506,7 +597,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -804,6 +895,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -813,6 +913,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.40" @@ -849,6 +969,12 @@ dependencies = [ "r2d2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -917,13 +1043,22 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "rhai" version = "1.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2780e813b755850e50b178931aaf94ed24f6817f46aaaf5d21c13c12d939a249" dependencies = [ - "ahash", + "ahash 0.8.12", "bitflags", "instant", "no-std-compat", @@ -944,7 +1079,44 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "rhailib-macros" +version = "0.1.0" +dependencies = [ + "rhai", + "serde", +] + +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -954,7 +1126,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d" dependencies = [ "arrayvec", + "borsh", + "bytes", "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", ] [[package]] @@ -990,6 +1168,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "serde" version = "1.0.219" @@ -1007,7 +1191,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1049,6 +1233,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "1.0.1" @@ -1137,7 +1327,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.104", ] [[package]] @@ -1146,6 +1336,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.104" @@ -1157,6 +1358,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "thin-vec" version = "0.2.14" @@ -1180,7 +1387,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1235,7 +1442,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1277,6 +1484,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "tst" version = "0.1.0" @@ -1390,7 +1614,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -1412,7 +1636,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1468,7 +1692,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1479,7 +1703,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1588,6 +1812,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -1597,6 +1830,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zerocopy" version = "0.8.26" @@ -1614,5 +1856,5 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] diff --git a/heromodels/Cargo.toml b/heromodels/Cargo.toml index 99be424..878bcdd 100644 --- a/heromodels/Cargo.toml +++ b/heromodels/Cargo.toml @@ -14,12 +14,14 @@ ourdb = { path = "../../herolib_rust/packages/data/ourdb" } tst = { path = "../../herolib_rust/packages/data/tst" } heromodels-derive = { path = "../heromodels-derive" } heromodels_core = { path = "../heromodels_core" } +rhailib-macros = { path = "../../herolib_rust/rhailib/src/macros" } rhai = { version = "1.21.0", features = [ "std", "sync", "decimal", "internals", ] } # Added "decimal" feature, sync for Arc> +rust_decimal = { version = "1.36", features = ["serde"] } strum = "0.26" strum_macros = "0.26" uuid = { version = "1.17.0", features = ["v4"] } diff --git a/heromodels/examples/legal_contract_example.rs b/heromodels/examples/legal_contract_example.rs index 7ae6ce7..e0e3b57 100644 --- a/heromodels/examples/legal_contract_example.rs +++ b/heromodels/examples/legal_contract_example.rs @@ -73,7 +73,7 @@ fn main() { // The `#[model]` derive handles `created_at` and `updated_at` in `base_data`. // `base_data.touch()` might be called internally by setters or needs explicit call if fields are set directly. - // For builder pattern, the final state of `base_data.updated_at` reflects the time of the last builder call if `touch()` is implicit. + // For builder pattern, the final state of `base_data.modified_at` reflects the time of the last builder call if `touch()` is implicit. // If not, one might call `contract.base_data.touch()` after building. println!("\n--- Initial Contract Details ---"); diff --git a/heromodels/src/models/access/rhai.rs b/heromodels/src/models/access/rhai.rs new file mode 100644 index 0000000..bfa63f8 --- /dev/null +++ b/heromodels/src/models/access/rhai.rs @@ -0,0 +1,148 @@ +use crate::db::Db; +use rhailib_macros::{ + register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, + register_authorized_get_by_id_fn, +}; +use rhai::plugin::*; +use rhai::{Dynamic, Engine, EvalAltResult, Module}; +use std::mem; +use std::sync::Arc; + +use heromodels::models::access::Access; +type RhaiAccess = Access; +use heromodels::db::hero::OurDB; +use heromodels::db::Collection; + +#[export_module] +mod rhai_access_module { + // --- Access Functions --- + #[rhai_fn(name = "new_access", return_raw)] + pub fn new_access() -> Result> { + let access = Access::new(); + Ok(access) + } + + /// Sets the access object_id + #[rhai_fn(name = "object_id", return_raw)] + pub fn set_object_id( + access: &mut RhaiAccess, + object_id: i64, + ) -> Result> { + let id = macros::id_from_i64_to_u32(object_id)?; + let owned_access = std::mem::take(access); + *access = owned_access.object_id(id); + Ok(access.clone()) + } + + /// Sets the circle public key + #[rhai_fn(name = "circle_public_key", return_raw)] + pub fn set_circle_pk( + access: &mut RhaiAccess, + circle_pk: String, + ) -> Result> { + let owned_access = std::mem::take(access); + *access = owned_access.circle_pk(circle_pk); + Ok(access.clone()) + } + + /// Sets the group id + #[rhai_fn(name = "group_id", return_raw)] + pub fn set_group_id( + access: &mut RhaiAccess, + group_id: i64, + ) -> Result> { + let id = macros::id_from_i64_to_u32(group_id)?; + let owned_access = std::mem::take(access); + *access = owned_access.group_id(id); + Ok(access.clone()) + } + + /// Sets the contact id + #[rhai_fn(name = "contact_id", return_raw)] + pub fn set_contact_id( + access: &mut RhaiAccess, + contact_id: i64, + ) -> Result> { + let id = macros::id_from_i64_to_u32(contact_id)?; + let owned_access = std::mem::take(access); + *access = owned_access.contact_id(id); + Ok(access.clone()) + } + + /// Sets the expiration time + #[rhai_fn(name = "expires_at", return_raw)] + pub fn set_expires_at( + access: &mut RhaiAccess, + expires_at: i64, + ) -> Result> { + let owned_access = std::mem::take(access); + *access = owned_access.expires_at(expires_at); + Ok(access.clone()) + } + + // Access Getters + #[rhai_fn(name = "get_access_id")] + pub fn get_access_id(access: &mut RhaiAccess) -> i64 { + access.base.id as i64 + } + + #[rhai_fn(name = "get_access_object_id")] + pub fn get_access_object_id(access: &mut RhaiAccess) -> i64 { + access.object_id as i64 + } + + #[rhai_fn(name = "get_access_circle_pk")] + pub fn get_access_circle_pk(access: &mut RhaiAccess) -> String { + access.circle_pk.clone() + } + + #[rhai_fn(name = "get_access_group_id")] + pub fn get_access_group_id(access: &mut RhaiAccess) -> i64 { + access.group_id as i64 + } + + #[rhai_fn(name = "get_access_contact_id")] + pub fn get_access_contact_id(access: &mut RhaiAccess) -> i64 { + access.contact_id as i64 + } + + #[rhai_fn(name = "get_access_expires_at")] + pub fn get_access_expires_at(access: &mut RhaiAccess) -> i64 { + access.expires_at + } + + #[rhai_fn(name = "get_access_created_at")] + pub fn get_access_created_at(access: &mut RhaiAccess) -> i64 { + access.base.created_at + } + + #[rhai_fn(name = "get_access_modified_at")] + pub fn get_access_modified_at(access: &mut RhaiAccess) -> i64 { + access.base.modified_at + } +} + +pub fn register_access_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_access_module); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_access", + resource_type_str: "Access", + rhai_return_rust_type: heromodels::models::access::Access + ); + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_access", + resource_type_str: "Access", + rhai_return_rust_type: heromodels::models::access::Access + ); + register_authorized_delete_by_id_fn!( + module: &mut module, + rhai_fn_name: "delete_access", + resource_type_str: "Access", + rhai_return_rust_type: heromodels::models::access::Access + ); + + engine.register_global_module(module.into()); +} diff --git a/heromodels/src/models/biz/rhai.rs b/heromodels/src/models/biz/rhai.rs new file mode 100644 index 0000000..e65da8c --- /dev/null +++ b/heromodels/src/models/biz/rhai.rs @@ -0,0 +1,422 @@ +use heromodels::db::Db; +use macros::{ + register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, + register_authorized_get_by_id_fn, +}; +use rhai::plugin::*; +use rhai::{Array, Engine, EvalAltResult, Module, Position, FLOAT, INT}; +use std::mem; +use std::sync::Arc; + +use heromodels::db::hero::OurDB; +use heromodels::db::Collection; +use heromodels::models::biz::product::{Product, ProductComponent, ProductStatus, ProductType}; +use heromodels::models::biz::company::{BusinessType, Company, CompanyStatus}; +use heromodels::models::biz::sale::{Sale, SaleItem, SaleStatus}; +use heromodels::models::biz::shareholder::{Shareholder, ShareholderType}; + +type RhaiProduct = Product; +type RhaiProductComponent = ProductComponent; +type RhaiCompany = Company; +type RhaiSale = Sale; +type RhaiSaleItem = SaleItem; +type RhaiShareholder = Shareholder; + +#[export_module] +mod rhai_product_component_module { + use super::{RhaiProductComponent, INT}; + + #[rhai_fn(name = "new_product_component", return_raw)] + pub fn new_product_component() -> Result> { + Ok(ProductComponent::new()) + } + + #[rhai_fn(name = "name", return_raw)] + pub fn set_name( + component: &mut RhaiProductComponent, + name: String, + ) -> Result> { + let owned = std::mem::take(component); + *component = owned.name(name); + Ok(component.clone()) + } + + #[rhai_fn(name = "description", return_raw)] + pub fn set_description( + component: &mut RhaiProductComponent, + description: String, + ) -> Result> { + let owned = std::mem::take(component); + *component = owned.description(description); + Ok(component.clone()) + } + + #[rhai_fn(name = "quantity", return_raw)] + pub fn set_quantity( + component: &mut RhaiProductComponent, + quantity: INT, + ) -> Result> { + let owned = std::mem::take(component); + *component = owned.quantity(quantity as u32); + Ok(component.clone()) + } + + // --- Getters --- + #[rhai_fn(name = "get_name")] + pub fn get_name(c: &mut RhaiProductComponent) -> String { + c.name.clone() + } + #[rhai_fn(name = "get_description")] + pub fn get_description(c: &mut RhaiProductComponent) -> String { + c.description.clone() + } + #[rhai_fn(name = "get_quantity")] + pub fn get_quantity(c: &mut RhaiProductComponent) -> INT { + c.quantity as INT + } +} + +#[export_module] +mod rhai_product_module { + use super::{Array, ProductStatus, ProductType, RhaiProduct, RhaiProductComponent, FLOAT, INT}; + + #[rhai_fn(name = "new_product", return_raw)] + pub fn new_product() -> Result> { + Ok(Product::new()) + } + + // --- Setters --- + #[rhai_fn(name = "name", return_raw)] + pub fn set_name( + product: &mut RhaiProduct, + name: String, + ) -> Result> { + let owned = std::mem::take(product); + *product = owned.name(name); + Ok(product.clone()) + } + + #[rhai_fn(name = "description", return_raw)] + pub fn set_description( + product: &mut RhaiProduct, + description: String, + ) -> Result> { + let owned = std::mem::take(product); + *product = owned.description(description); + Ok(product.clone()) + } + + #[rhai_fn(name = "price", return_raw)] + pub fn set_price( + product: &mut RhaiProduct, + price: FLOAT, + ) -> Result> { + let owned = std::mem::take(product); + *product = owned.price(price); + Ok(product.clone()) + } + + #[rhai_fn(name = "category", return_raw)] + pub fn set_category( + product: &mut RhaiProduct, + category: String, + ) -> Result> { + let owned = std::mem::take(product); + *product = owned.category(category); + Ok(product.clone()) + } + + #[rhai_fn(name = "max_amount", return_raw)] + pub fn set_max_amount( + product: &mut RhaiProduct, + max_amount: INT, + ) -> Result> { + let owned = std::mem::take(product); + *product = owned.max_amount(max_amount as u32); + Ok(product.clone()) + } + + #[rhai_fn(name = "purchase_till", return_raw)] + pub fn set_purchase_till( + product: &mut RhaiProduct, + purchase_till: INT, + ) -> Result> { + let owned = std::mem::take(product); + *product = owned.purchase_till(purchase_till); + Ok(product.clone()) + } + + #[rhai_fn(name = "active_till", return_raw)] + pub fn set_active_till( + product: &mut RhaiProduct, + active_till: INT, + ) -> Result> { + let owned = std::mem::take(product); + *product = owned.active_till(active_till); + Ok(product.clone()) + } + + #[rhai_fn(name = "type", return_raw)] + pub fn set_type( + product: &mut RhaiProduct, + type_str: String, + ) -> Result> { + let product_type = match type_str.to_lowercase().as_str() { + "physical" => ProductType::Physical, + "digital" => ProductType::Digital, + "service" => ProductType::Service, + "subscription" => ProductType::Subscription, + _ => { + return Err(EvalAltResult::ErrorSystem( + "Invalid ProductType".to_string(), + "Must be one of: Physical, Digital, Service, Subscription".into(), + ) + .into()) + } + }; + let owned = std::mem::take(product); + *product = owned.product_type(product_type); + Ok(product.clone()) + } + + #[rhai_fn(name = "status", return_raw)] + pub fn set_status( + product: &mut RhaiProduct, + status_str: String, + ) -> Result> { + let status = match status_str.to_lowercase().as_str() { + "active" => ProductStatus::Active, + "inactive" => ProductStatus::Inactive, + "discontinued" => ProductStatus::Discontinued, + _ => { + return Err(EvalAltResult::ErrorSystem( + "Invalid ProductStatus".to_string(), + "Must be one of: Active, Inactive, Discontinued".into(), + ) + .into()) + } + }; + let owned = std::mem::take(product); + *product = owned.status(status); + Ok(product.clone()) + } + + #[rhai_fn(name = "add_component", return_raw)] + pub fn add_component( + product: &mut RhaiProduct, + component: RhaiProductComponent, + ) -> Result> { + let owned = std::mem::take(product); + *product = owned.add_component(component); + Ok(product.clone()) + } + + #[rhai_fn(name = "set_components", return_raw)] + pub fn set_components( + product: &mut RhaiProduct, + components: Array, + ) -> Result> { + let mut product_components = Vec::new(); + for component_dynamic in components { + if let Ok(component) = component_dynamic.try_cast::() { + product_components.push(component); + } else { + return Err(EvalAltResult::ErrorSystem( + "Invalid component type".to_string(), + "All components must be ProductComponent objects".into(), + ) + .into()); + } + } + let owned = std::mem::take(product); + *product = owned.components(product_components); + Ok(product.clone()) + } + + // --- Getters --- + #[rhai_fn(name = "get_id")] + pub fn get_id(p: &mut RhaiProduct) -> i64 { + p.base.id as i64 + } + #[rhai_fn(name = "get_name")] + pub fn get_name(p: &mut RhaiProduct) -> String { + p.name.clone() + } + #[rhai_fn(name = "get_description")] + pub fn get_description(p: &mut RhaiProduct) -> String { + p.description.clone() + } + #[rhai_fn(name = "get_price")] + pub fn get_price(p: &mut RhaiProduct) -> FLOAT { + p.price + } + #[rhai_fn(name = "get_category")] + pub fn get_category(p: &mut RhaiProduct) -> String { + p.category.clone() + } + #[rhai_fn(name = "get_max_amount")] + pub fn get_max_amount(p: &mut RhaiProduct) -> INT { + p.max_amount as INT + } + #[rhai_fn(name = "get_purchase_till")] + pub fn get_purchase_till(p: &mut RhaiProduct) -> INT { + p.purchase_till + } + #[rhai_fn(name = "get_active_till")] + pub fn get_active_till(p: &mut RhaiProduct) -> INT { + p.active_till + } + #[rhai_fn(name = "get_type")] + pub fn get_type(p: &mut RhaiProduct) -> String { + format!("{:?}", p.product_type) + } + #[rhai_fn(name = "get_status")] + pub fn get_status(p: &mut RhaiProduct) -> String { + format!("{:?}", p.status) + } + #[rhai_fn(name = "get_components")] + pub fn get_components(p: &mut RhaiProduct) -> Array { + p.components + .iter() + .map(|c| rhai::Dynamic::from(c.clone())) + .collect() + } +} + +pub fn register_product_rhai_module(engine: &mut Engine) { + let mut product_module = exported_module!(rhai_product_module); + let mut component_module = exported_module!(rhai_product_component_module); + + register_authorized_create_by_id_fn!( + product_module: &mut product_module, + rhai_fn_name: "save_product", + resource_type_str: "Product", + rhai_return_rust_type: heromodels::models::biz::product::Product + ); + register_authorized_get_by_id_fn!( + product_module: &mut product_module, + rhai_fn_name: "get_product", + resource_type_str: "Product", + rhai_return_rust_type: heromodels::models::biz::product::Product + ); + register_authorized_delete_by_id_fn!( + product_module: &mut product_module, + rhai_fn_name: "delete_product", + resource_type_str: "Product", + rhai_return_rust_type: heromodels::models::biz::product::Product + ); + + engine.register_global_module(product_module.into()); + engine.register_global_module(component_module.into()); +} + +// Company Rhai wrapper functions +#[export_module] +mod rhai_company_module { + use super::{BusinessType, CompanyStatus, RhaiCompany}; + + #[rhai_fn(name = "new_company", return_raw)] + pub fn new_company() -> Result> { + Ok(Company::new()) + } + + #[rhai_fn(name = "name", return_raw)] + pub fn set_name( + company: &mut RhaiCompany, + name: String, + ) -> Result> { + let owned = std::mem::take(company); + *company = owned.name(name); + Ok(company.clone()) + } + + #[rhai_fn(name = "get_company_id")] + pub fn get_company_id(company: &mut RhaiCompany) -> i64 { + company.id() as i64 + } + + #[rhai_fn(name = "get_company_name")] + pub fn get_company_name(company: &mut RhaiCompany) -> String { + company.name().clone() + } +} + +pub fn register_company_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_company_module); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_company", + resource_type_str: "Company", + rhai_return_rust_type: heromodels::models::biz::company::Company + ); + + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_company", + resource_type_str: "Company", + rhai_return_rust_type: heromodels::models::biz::company::Company + ); + + engine.register_global_module(module.into()); +} + +// Sale Rhai wrapper functions +#[export_module] +mod rhai_sale_module { + use super::{RhaiSale, RhaiSaleItem, SaleStatus}; + + #[rhai_fn(name = "new_sale", return_raw)] + pub fn new_sale() -> Result> { + Ok(Sale::new()) + } + + #[rhai_fn(name = "new_sale_item", return_raw)] + pub fn new_sale_item() -> Result> { + Ok(SaleItem::new()) + } + + #[rhai_fn(name = "company_id", return_raw)] + pub fn set_sale_company_id(sale: &mut RhaiSale, company_id: i64) -> Result> { + let owned = std::mem::take(sale); + *sale = owned.company_id(company_id as u32); + Ok(sale.clone()) + } + + #[rhai_fn(name = "total_amount", return_raw)] + pub fn set_sale_total_amount(sale: &mut RhaiSale, total_amount: f64) -> Result> { + let owned = std::mem::take(sale); + *sale = owned.total_amount(total_amount); + Ok(sale.clone()) + } + + #[rhai_fn(name = "get_sale_id")] + pub fn get_sale_id(sale: &mut RhaiSale) -> i64 { + sale.id() as i64 + } + + #[rhai_fn(name = "get_sale_total_amount")] + pub fn get_sale_total_amount(sale: &mut RhaiSale) -> f64 { + sale.total_amount() + } +} + +pub fn register_sale_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_sale_module); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_sale", + resource_type_str: "Sale", + rhai_return_rust_type: heromodels::models::biz::sale::Sale + ); + + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_sale", + resource_type_str: "Sale", + rhai_return_rust_type: heromodels::models::biz::sale::Sale + ); + + engine.register_global_module(module.into()); +} diff --git a/heromodels/src/models/calendar/rhai.rs b/heromodels/src/models/calendar/rhai.rs new file mode 100644 index 0000000..9d73515 --- /dev/null +++ b/heromodels/src/models/calendar/rhai.rs @@ -0,0 +1,246 @@ +use crate::db::Db; +use rhailib_macros::{ + register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, + register_authorized_get_by_id_fn, +}; +use rhai::plugin::*; +use rhai::{Array, Dynamic, Engine, EvalAltResult, Module}; +use std::mem; +use std::sync::Arc; + +use crate::models::calendar::{AttendanceStatus, Attendee, Calendar, Event}; +type RhaiCalendar = Calendar; +type RhaiEvent = Event; +type RhaiAttendee = Attendee; +use crate::db::hero::OurDB; +use crate::db::Collection; + +#[export_module] +mod rhai_calendar_module { + use super::{AttendanceStatus, RhaiAttendee, RhaiCalendar, RhaiEvent}; + + // --- Attendee Builder --- + #[rhai_fn(name = "new_attendee", return_raw)] + pub fn new_attendee(contact_id: i64) -> Result> { + Ok(Attendee::new(contact_id as u32)) + } + + #[rhai_fn(name = "status", return_raw)] + pub fn set_attendee_status( + attendee: &mut RhaiAttendee, + status_str: String, + ) -> Result> { + let status = match status_str.to_lowercase().as_str() { + "accepted" => AttendanceStatus::Accepted, + "declined" => AttendanceStatus::Declined, + "tentative" => AttendanceStatus::Tentative, + "noresponse" => AttendanceStatus::NoResponse, + _ => { + return Err(EvalAltResult::ErrorSystem( + "Invalid Status".to_string(), + "Must be one of: Accepted, Declined, Tentative, NoResponse".into(), + ) + .into()) + } + }; + let owned = std::mem::take(attendee); + *attendee = owned.status(status); + Ok(attendee.clone()) + } + + // --- Event Builder --- + #[rhai_fn(name = "new_event", return_raw)] + pub fn new_event() -> Result> { + Ok(Event::new()) + } + + #[rhai_fn(name = "title", return_raw)] + pub fn set_event_title( + event: &mut RhaiEvent, + title: String, + ) -> Result> { + let owned = std::mem::take(event); + *event = owned.title(title); + Ok(event.clone()) + } + + #[rhai_fn(name = "description", return_raw)] + pub fn set_event_description( + event: &mut RhaiEvent, + description: String, + ) -> Result> { + let owned = std::mem::take(event); + *event = owned.description(description); + Ok(event.clone()) + } + + #[rhai_fn(name = "location", return_raw)] + pub fn set_event_location( + event: &mut RhaiEvent, + location: String, + ) -> Result> { + let owned = std::mem::take(event); + *event = owned.location(location); + Ok(event.clone()) + } + + #[rhai_fn(name = "add_attendee", return_raw)] + pub fn add_event_attendee( + event: &mut RhaiEvent, + attendee: RhaiAttendee, + ) -> Result> { + let owned = std::mem::take(event); + *event = owned.add_attendee(attendee); + Ok(event.clone()) + } + + #[rhai_fn(name = "reschedule", return_raw)] + pub fn reschedule_event( + event: &mut RhaiEvent, + start_time: i64, + end_time: i64, + ) -> Result> { + let owned = std::mem::take(event); + *event = owned.reschedule(start_time, end_time); + Ok(event.clone()) + } + + // --- Calendar Builder --- + #[rhai_fn(name = "new_calendar", return_raw)] + pub fn new_calendar(name: String) -> Result> { + Ok(Calendar::new().name(name)) + } + + #[rhai_fn(name = "calendar_name", return_raw)] + pub fn set_calendar_name( + calendar: &mut RhaiCalendar, + name: String, + ) -> Result> { + let owned = std::mem::take(calendar); + *calendar = owned.name(name); + Ok(calendar.clone()) + } + + #[rhai_fn(name = "calendar_description", return_raw)] + pub fn set_calendar_description( + calendar: &mut RhaiCalendar, + description: String, + ) -> Result> { + let owned = std::mem::take(calendar); + *calendar = owned.description(description); + Ok(calendar.clone()) + } + + #[rhai_fn(name = "add_event", return_raw)] + pub fn add_calendar_event( + calendar: &mut RhaiCalendar, + event_id: i64, + ) -> Result> { + let owned = std::mem::take(calendar); + *calendar = owned.add_event(event_id as u32); + Ok(calendar.clone()) + } + + // --- Getters --- + // Calendar + #[rhai_fn(name = "get_calendar_id")] + pub fn get_calendar_id(c: &mut RhaiCalendar) -> i64 { + c.base.id as i64 + } + #[rhai_fn(name = "get_calendar_name")] + pub fn get_calendar_name(c: &mut RhaiCalendar) -> String { + c.name.clone() + } + #[rhai_fn(name = "get_calendar_description")] + pub fn get_calendar_description(c: &mut RhaiCalendar) -> Option { + c.description.clone() + } + #[rhai_fn(name = "get_calendar_events")] + pub fn get_calendar_events(c: &mut RhaiCalendar) -> Array { + c.events.iter().map(|id| Dynamic::from(*id as i64)).collect() + } + + // Event + #[rhai_fn(name = "get_event_id")] + pub fn get_event_id(e: &mut RhaiEvent) -> i64 { + e.base.id as i64 + } + #[rhai_fn(name = "get_event_title")] + pub fn get_event_title(e: &mut RhaiEvent) -> String { + e.title.clone() + } + #[rhai_fn(name = "get_event_description")] + pub fn get_event_description(e: &mut RhaiEvent) -> Option { + e.description.clone() + } + #[rhai_fn(name = "get_event_start_time")] + pub fn get_event_start_time(e: &mut RhaiEvent) -> i64 { + e.start_time + } + #[rhai_fn(name = "get_event_end_time")] + pub fn get_event_end_time(e: &mut RhaiEvent) -> i64 { + e.end_time + } + #[rhai_fn(name = "get_event_attendees")] + pub fn get_event_attendees(e: &mut RhaiEvent) -> Array { + e.attendees.iter().map(|a| Dynamic::from(a.clone())).collect() + } + #[rhai_fn(name = "get_event_location")] + pub fn get_event_location(e: &mut RhaiEvent) -> Option { + e.location.clone() + } + + // Attendee + #[rhai_fn(name = "get_attendee_contact_id")] + pub fn get_attendee_contact_id(a: &mut RhaiAttendee) -> i64 { + a.contact_id as i64 + } + #[rhai_fn(name = "get_attendee_status")] + pub fn get_attendee_status(a: &mut RhaiAttendee) -> String { + format!("{:?}", a.status) + } +} + +pub fn register_calendar_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_calendar_module); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_calendar", + resource_type_str: "Calendar", + rhai_return_rust_type: heromodels::models::calendar::Calendar + ); + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_calendar", + resource_type_str: "Calendar", + rhai_return_rust_type: heromodels::models::calendar::Calendar + ); + register_authorized_delete_by_id_fn!( + module: &mut module, + rhai_fn_name: "delete_calendar", + resource_type_str: "Calendar", + rhai_return_rust_type: heromodels::models::calendar::Calendar + ); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_event", + resource_type_str: "Event", + rhai_return_rust_type: heromodels::models::calendar::Event + ); + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_event", + resource_type_str: "Event", + rhai_return_rust_type: heromodels::models::calendar::Event + ); + register_authorized_delete_by_id_fn!( + module: &mut module, + rhai_fn_name: "delete_event", + resource_type_str: "Event", + rhai_return_rust_type: heromodels::models::calendar::Event + ); + + engine.register_global_module(module.into()); +} diff --git a/heromodels/src/models/circle/rhai.rs b/heromodels/src/models/circle/rhai.rs index d51c1cb..d8c37cd 100644 --- a/heromodels/src/models/circle/rhai.rs +++ b/heromodels/src/models/circle/rhai.rs @@ -1,412 +1,155 @@ use crate::db::Db; +use rhailib_macros::{ + register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn, +}; use rhai::plugin::*; -use rhai::{Array, CustomType, Dynamic, Engine, EvalAltResult, INT, Module, Position}; -use std::mem; +use rhai::{Array, Dynamic, Engine, EvalAltResult, Map, Module}; +use std::collections::HashMap; use std::sync::Arc; -use super::circle::{Circle, ThemeData}; +use crate::models::circle::Circle; type RhaiCircle = Circle; -type RhaiThemeData = ThemeData; - -use crate::db::Collection; use crate::db::hero::OurDB; -use serde::Serialize; -use serde_json; - -/// Registers a `.json()` method for any type `T` that implements the required traits. -fn register_json_method(engine: &mut Engine) -where - T: CustomType + Clone + Serialize, -{ - let to_json_fn = |obj: &mut T| -> Result> { - serde_json::to_string(obj).map_err(|e| e.to_string().into()) - }; - engine.build_type::().register_fn("json", to_json_fn); -} - -// Helper to convert i64 from Rhai to u32 for IDs -fn id_from_i64_to_u32(id_i64: i64) -> Result> { - u32::try_from(id_i64).map_err(|_| { - Box::new(EvalAltResult::ErrorArithmetic( - format!("Failed to convert ID '{}' to u32", id_i64).into(), - Position::NONE, - )) - }) -} - -#[export_module] -mod rhai_theme_data_module { - #[rhai_fn(name = "new_theme_data")] - pub fn new_theme_data() -> RhaiThemeData { - ThemeData::default() - } - - // --- Setters for ThemeData --- - #[rhai_fn(name = "primary_color", return_raw, global, pure)] - pub fn set_primary_color( - theme: &mut RhaiThemeData, - color: String, - ) -> Result> { - let mut owned_theme = mem::take(theme); - owned_theme.primary_color = color; - *theme = owned_theme; - Ok(theme.clone()) - } - - #[rhai_fn(name = "background_color", return_raw, global, pure)] - pub fn set_background_color( - theme: &mut RhaiThemeData, - color: String, - ) -> Result> { - let mut owned_theme = mem::take(theme); - owned_theme.background_color = color; - *theme = owned_theme; - Ok(theme.clone()) - } - - #[rhai_fn(name = "background_pattern", return_raw, global, pure)] - pub fn set_background_pattern( - theme: &mut RhaiThemeData, - pattern: String, - ) -> Result> { - let mut owned_theme = mem::take(theme); - owned_theme.background_pattern = pattern; - *theme = owned_theme; - Ok(theme.clone()) - } - - #[rhai_fn(name = "logo_symbol", return_raw, global, pure)] - pub fn set_logo_symbol( - theme: &mut RhaiThemeData, - symbol: String, - ) -> Result> { - let mut owned_theme = mem::take(theme); - owned_theme.logo_symbol = symbol; - *theme = owned_theme; - Ok(theme.clone()) - } - - #[rhai_fn(name = "logo_url", return_raw, global, pure)] - pub fn set_logo_url( - theme: &mut RhaiThemeData, - url: String, - ) -> Result> { - let mut owned_theme = mem::take(theme); - owned_theme.logo_url = url; - *theme = owned_theme; - Ok(theme.clone()) - } - - #[rhai_fn(name = "nav_dashboard_visible", return_raw, global, pure)] - pub fn set_nav_dashboard_visible( - theme: &mut RhaiThemeData, - visible: bool, - ) -> Result> { - let mut owned_theme = mem::take(theme); - owned_theme.nav_dashboard_visible = visible; - *theme = owned_theme; - Ok(theme.clone()) - } - - #[rhai_fn(name = "nav_timeline_visible", return_raw, global, pure)] - pub fn set_nav_timeline_visible( - theme: &mut RhaiThemeData, - visible: bool, - ) -> Result> { - let mut owned_theme = mem::take(theme); - owned_theme.nav_timeline_visible = visible; - *theme = owned_theme; - Ok(theme.clone()) - } - - // --- Getters for ThemeData --- - #[rhai_fn(name = "get_primary_color", pure)] - pub fn get_primary_color(theme: &mut RhaiThemeData) -> String { - theme.primary_color.clone() - } - - #[rhai_fn(name = "get_background_color", pure)] - pub fn get_background_color(theme: &mut RhaiThemeData) -> String { - theme.background_color.clone() - } - - #[rhai_fn(name = "get_background_pattern", pure)] - pub fn get_background_pattern(theme: &mut RhaiThemeData) -> String { - theme.background_pattern.clone() - } - - #[rhai_fn(name = "get_logo_symbol", pure)] - pub fn get_logo_symbol(theme: &mut RhaiThemeData) -> String { - theme.logo_symbol.clone() - } - - #[rhai_fn(name = "get_logo_url", pure)] - pub fn get_logo_url(theme: &mut RhaiThemeData) -> String { - theme.logo_url.clone() - } - - #[rhai_fn(name = "get_nav_dashboard_visible", pure)] - pub fn get_nav_dashboard_visible(theme: &mut RhaiThemeData) -> bool { - theme.nav_dashboard_visible - } - - #[rhai_fn(name = "get_nav_timeline_visible", pure)] - pub fn get_nav_timeline_visible(theme: &mut RhaiThemeData) -> bool { - theme.nav_timeline_visible - } -} +use crate::db::Collection; +use crate::models::circle::ThemeData; #[export_module] mod rhai_circle_module { - // --- Circle Functions --- - #[rhai_fn(name = "new_circle")] - pub fn new_circle() -> RhaiCircle { - Circle::new() + use super::RhaiCircle; + + // this one configures the users own circle + #[rhai_fn(name = "configure", return_raw)] + pub fn configure() -> Result> { + Ok(Circle::new()) } - /// Sets the circle title - #[rhai_fn(name = "title", return_raw, global, pure)] - pub fn circle_title( + #[rhai_fn(name = "new_circle", return_raw)] + pub fn new_circle() -> Result> { + Ok(Circle::new()) + } + + #[rhai_fn(name = "set_title", return_raw)] + pub fn set_title( circle: &mut RhaiCircle, title: String, ) -> Result> { - let owned_circle = mem::take(circle); - *circle = owned_circle.title(title); + let owned = std::mem::take(circle); + *circle = owned.title(title); Ok(circle.clone()) } - /// Sets the circle ws_url - #[rhai_fn(name = "ws_url", return_raw, global, pure)] - pub fn circle_ws_url( + #[rhai_fn(name = "set_ws_url", return_raw)] + pub fn set_ws_url( circle: &mut RhaiCircle, ws_url: String, ) -> Result> { - let owned_circle = mem::take(circle); - *circle = owned_circle.ws_url(ws_url); + let owned = std::mem::take(circle); + *circle = owned.ws_url(ws_url); Ok(circle.clone()) } - /// Sets the circle description - #[rhai_fn(name = "description", return_raw, global, pure)] - pub fn circle_description( + #[rhai_fn(name = "set_description", return_raw)] + pub fn set_description( circle: &mut RhaiCircle, description: String, ) -> Result> { - let owned_circle = mem::take(circle); - *circle = owned_circle.description(description); + let owned = std::mem::take(circle); + *circle = owned.description(description); Ok(circle.clone()) } - /// Sets the circle logo - #[rhai_fn(name = "logo", return_raw, global, pure)] - pub fn circle_logo( + #[rhai_fn(name = "set_logo", return_raw)] + pub fn set_logo( circle: &mut RhaiCircle, logo: String, ) -> Result> { - let owned_circle = mem::take(circle); - *circle = owned_circle.logo(logo); + let owned = std::mem::take(circle); + *circle = owned.logo(logo); Ok(circle.clone()) } - /// Sets the circle theme - #[rhai_fn(name = "theme", return_raw, global, pure)] - pub fn circle_theme( + #[rhai_fn(name = "set_theme", return_raw)] + pub fn set_theme( circle: &mut RhaiCircle, - theme: RhaiThemeData, + theme: ThemeData, ) -> Result> { - let owned_circle = mem::take(circle); - *circle = owned_circle.theme(theme); + let owned = std::mem::take(circle); + *circle = owned.theme(theme); Ok(circle.clone()) } - /// Adds an attendee to the circle - #[rhai_fn(name = "add_circle", return_raw, global, pure)] - pub fn circle_add_circle( + #[rhai_fn(name = "add_circle", return_raw)] + pub fn add_circle( circle: &mut RhaiCircle, - added_circle: String, + new_circle: String, ) -> Result> { - let owned_circle = mem::take(circle); - *circle = owned_circle.add_circle(added_circle); + let owned = std::mem::take(circle); + *circle = owned.add_circle(new_circle); Ok(circle.clone()) } - /// Adds an attendee to the circle - #[rhai_fn(name = "add_member", return_raw, global, pure)] - pub fn circle_add_member( + #[rhai_fn(name = "add_member", return_raw)] + pub fn add_member( circle: &mut RhaiCircle, - added_member: String, + member: String, ) -> Result> { - let owned_circle = mem::take(circle); - *circle = owned_circle.add_member(added_member); + let owned = std::mem::take(circle); + *circle = owned.add_member(member); Ok(circle.clone()) } - // Circle Getters - #[rhai_fn(name = "get_id", pure)] - pub fn get_circle_id(circle: &mut RhaiCircle) -> i64 { - circle.base_data.id as i64 + // --- Getters --- + #[rhai_fn(name = "get_id")] + pub fn get_id(c: &mut RhaiCircle) -> i64 { + c.base_data.id as i64 } - #[rhai_fn(name = "get_created_at", pure)] - pub fn get_circle_created_at(circle: &mut RhaiCircle) -> i64 { - circle.base_data.created_at + #[rhai_fn(name = "get_title")] + pub fn get_title(c: &mut RhaiCircle) -> String { + c.title.clone() } - #[rhai_fn(name = "get_modified_at", pure)] - pub fn get_circle_modified_at(circle: &mut RhaiCircle) -> i64 { - circle.base_data.modified_at + #[rhai_fn(name = "get_ws_url")] + pub fn get_ws_url(c: &mut RhaiCircle) -> String { + c.ws_url.clone() } - - #[rhai_fn(name = "get_title", pure)] - pub fn get_circle_title(circle: &mut RhaiCircle) -> String { - circle.title.clone() + #[rhai_fn(name = "get_description")] + pub fn get_description(c: &mut RhaiCircle) -> Option { + c.description.clone() } - #[rhai_fn(name = "get_description", pure)] - pub fn get_circle_description(circle: &mut RhaiCircle) -> Option { - circle.description.clone() + #[rhai_fn(name = "get_logo")] + pub fn get_logo(c: &mut RhaiCircle) -> Option { + c.logo.clone() } - #[rhai_fn(name = "get_circles", pure)] - pub fn get_circle_circles(circle: &mut RhaiCircle) -> Vec { - circle.circles.clone() + #[rhai_fn(name = "get_circles")] + pub fn get_circles(c: &mut RhaiCircle) -> Array { + c.circles.iter().map(|s| Dynamic::from(s.clone())).collect() } - #[rhai_fn(name = "get_ws_url", pure)] - pub fn get_circle_ws_url(circle: &mut RhaiCircle) -> String { - circle.ws_url.clone() - } - #[rhai_fn(name = "get_logo", pure)] - pub fn get_circle_logo(circle: &mut RhaiCircle) -> Option { - circle.logo.clone() - } - #[rhai_fn(name = "get_theme", pure)] - pub fn get_circle_theme(circle: &mut RhaiCircle) -> RhaiThemeData { - circle.theme.clone() + #[rhai_fn(name = "get_members")] + pub fn get_members(c: &mut RhaiCircle) -> Array { + c.members.iter().map(|s| Dynamic::from(s.clone())).collect() } } -pub fn register_circle_rhai_module(engine: &mut Engine, db: Arc) { - engine.build_type::(); - engine.build_type::(); +pub fn register_circle_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_circle_module); - let mut db_module = Module::new(); - let circle_module = exported_module!(rhai_circle_module); - let theme_data_module = exported_module!(rhai_theme_data_module); - - engine.register_global_module(circle_module.into()); - engine.register_global_module(theme_data_module.into()); - - register_json_method::(engine); - register_json_method::(engine); - - // Manually register database functions as they need to capture 'db' - let db_clone_set_circle = db.clone(); - db_module.set_native_fn( - "save_circle", - move |circle: Circle| -> Result> { - let result = db_clone_set_circle.set(&circle).map_err(|e| { - Box::new(EvalAltResult::ErrorRuntime( - format!("DB Error set_circle: {}", e).into(), - Position::NONE, - )) - })?; - Ok(result.1) - }, + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_circle", + resource_type_str: "Circle", + rhai_return_rust_type: crate::models::circle::Circle + ); + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_circle", + resource_type_str: "Circle", + rhai_return_rust_type: crate::models::circle::Circle + ); + register_authorized_delete_by_id_fn!( + module: &mut module, + rhai_fn_name: "delete_circle", + resource_type_str: "Circle", + rhai_return_rust_type: crate::models::circle::Circle ); - let db_clone_delete_circle = db.clone(); - db_module.set_native_fn( - "delete_circle", - move |circle: Circle| -> Result<(), Box> { - let result = db_clone_delete_circle - .collection::() - .expect("can open circle collection") - .delete_by_id(circle.base_data.id) - .expect("can delete circle"); - Ok(result) - }, - ); - - let db_clone_get_circle = db.clone(); - db_module.set_native_fn( - "get_circle", - move || -> Result> { - let all_circles: Vec = db_clone_get_circle.get_all().map_err(|e| { - Box::new(EvalAltResult::ErrorRuntime( - format!("DB Error get_circle: {}", e).into(), - Position::NONE, - )) - })?; - - if let Some(first_circle) = all_circles.first() { - Ok(first_circle.clone()) - } else { - Err(Box::new(EvalAltResult::ErrorRuntime( - "Circle not found".into(), - Position::NONE, - ))) - } - }, - ); - - // --- Collection DB Functions --- - let db_clone = db.clone(); - db_module.set_native_fn( - "save_circle", - move |circle: RhaiCircle| -> Result> { - let result = db_clone.set(&circle).map_err(|e| { - Box::new(EvalAltResult::ErrorRuntime( - format!("DB Error: {:?}", e).into(), - Position::NONE, - )) - })?; - Ok(result.1) - }, - ); - - let db_clone_get_circle_by_id = db.clone(); - db_module.set_native_fn( - "get_circle_by_id", - move |id_i64: INT| -> Result> { - let id_u32 = id_from_i64_to_u32(id_i64)?; - db_clone_get_circle_by_id - .get_by_id(id_u32) - .map_err(|e| { - Box::new(EvalAltResult::ErrorRuntime( - format!("DB Error get_circle_by_id: {}", e).into(), - Position::NONE, - )) - })? - .ok_or_else(|| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Circle with ID {} not found", id_u32).into(), - Position::NONE, - )) - }) - }, - ); - - let db_clone_list_circles = db.clone(); - db_module.set_native_fn( - "list_circles", - move || -> Result> { - let collection = db_clone_list_circles.collection::().map_err(|e| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Failed to get circle collection: {:?}", e).into(), - Position::NONE, - )) - })?; - let circles = collection.get_all().map_err(|e| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Failed to get all circles: {:?}", e).into(), - Position::NONE, - )) - })?; - let mut array = Array::new(); - for circle in circles { - array.push(Dynamic::from(circle)); - } - Ok(Dynamic::from(array)) - }, - ); - - engine.register_global_module(db_module.into()); - - println!("Successfully registered circle Rhai module using export_module approach."); + engine.register_global_module(module.into()); } diff --git a/heromodels/src/models/contact/rhai.rs b/heromodels/src/models/contact/rhai.rs new file mode 100644 index 0000000..b8f8ba8 --- /dev/null +++ b/heromodels/src/models/contact/rhai.rs @@ -0,0 +1,232 @@ +use crate::db::Db; +use rhailib_macros::{ + register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, + register_authorized_get_by_id_fn, +}; +use rhai::plugin::*; +use rhai::{Array, Dynamic, Engine, EvalAltResult, Module}; +use std::mem; +use std::sync::Arc; + +use crate::models::contact::{Contact, Group}; +type RhaiContact = Contact; +type RhaiGroup = Group; +use crate::db::hero::OurDB; +use crate::db::Collection; + +#[export_module] +mod rhai_contact_module { + use super::{RhaiContact, RhaiGroup}; + + // --- Contact Builder --- + #[rhai_fn(name = "new_contact", return_raw)] + pub fn new_contact() -> Result> { + Ok(Contact::new()) + } + + #[rhai_fn(name = "name", return_raw)] + pub fn set_contact_name( + contact: &mut RhaiContact, + name: String, + ) -> Result> { + let owned = std::mem::take(contact); + *contact = owned.name(name); + Ok(contact.clone()) + } + + #[rhai_fn(name = "description", return_raw)] + pub fn set_contact_description( + contact: &mut RhaiContact, + description: String, + ) -> Result> { + let owned = std::mem::take(contact); + *contact = owned.description(description); + Ok(contact.clone()) + } + + #[rhai_fn(name = "address", return_raw)] + pub fn set_contact_address( + contact: &mut RhaiContact, + address: String, + ) -> Result> { + let owned = std::mem::take(contact); + *contact = owned.address(address); + Ok(contact.clone()) + } + + #[rhai_fn(name = "phone", return_raw)] + pub fn set_contact_phone( + contact: &mut RhaiContact, + phone: String, + ) -> Result> { + let owned = std::mem::take(contact); + *contact = owned.phone(phone); + Ok(contact.clone()) + } + + #[rhai_fn(name = "email", return_raw)] + pub fn set_contact_email( + contact: &mut RhaiContact, + email: String, + ) -> Result> { + let owned = std::mem::take(contact); + *contact = owned.email(email); + Ok(contact.clone()) + } + + #[rhai_fn(name = "notes", return_raw)] + pub fn set_contact_notes( + contact: &mut RhaiContact, + notes: String, + ) -> Result> { + let owned = std::mem::take(contact); + *contact = owned.notes(notes); + Ok(contact.clone()) + } + + #[rhai_fn(name = "circle", return_raw)] + pub fn set_contact_circle( + contact: &mut RhaiContact, + circle: String, + ) -> Result> { + let owned = std::mem::take(contact); + *contact = owned.circle(circle); + Ok(contact.clone()) + } + + // --- Group Builder --- + #[rhai_fn(name = "new_group", return_raw)] + pub fn new_group() -> Result> { + Ok(Group::new()) + } + + #[rhai_fn(name = "group_name", return_raw)] + pub fn set_group_name( + group: &mut RhaiGroup, + name: String, + ) -> Result> { + let owned = std::mem::take(group); + *group = owned.name(name); + Ok(group.clone()) + } + + #[rhai_fn(name = "group_description", return_raw)] + pub fn set_group_description( + group: &mut RhaiGroup, + description: String, + ) -> Result> { + let owned = std::mem::take(group); + *group = owned.description(description); + Ok(group.clone()) + } + + #[rhai_fn(name = "add_contact", return_raw)] + pub fn add_group_contact( + group: &mut RhaiGroup, + contact_id: i64, + ) -> Result> { + let owned = std::mem::take(group); + *group = owned.add_contact(contact_id as u32); + Ok(group.clone()) + } + + // --- Getters --- + // Contact + #[rhai_fn(name = "get_contact_id")] + pub fn get_contact_id(c: &mut RhaiContact) -> i64 { + c.base.id as i64 + } + #[rhai_fn(name = "get_contact_name")] + pub fn get_contact_name(c: &mut RhaiContact) -> String { + c.name.clone() + } + #[rhai_fn(name = "get_contact_description")] + pub fn get_contact_description(c: &mut RhaiContact) -> Option { + c.description.clone() + } + #[rhai_fn(name = "get_contact_address")] + pub fn get_contact_address(c: &mut RhaiContact) -> String { + c.address.clone() + } + #[rhai_fn(name = "get_contact_phone")] + pub fn get_contact_phone(c: &mut RhaiContact) -> String { + c.phone.clone() + } + #[rhai_fn(name = "get_contact_email")] + pub fn get_contact_email(c: &mut RhaiContact) -> String { + c.email.clone() + } + #[rhai_fn(name = "get_contact_notes")] + pub fn get_contact_notes(c: &mut RhaiContact) -> Option { + c.notes.clone() + } + #[rhai_fn(name = "get_contact_circle")] + pub fn get_contact_circle(c: &mut RhaiContact) -> String { + c.circle.clone() + } + + // Group + #[rhai_fn(name = "get_group_id")] + pub fn get_group_id(g: &mut RhaiGroup) -> i64 { + g.base.id as i64 + } + #[rhai_fn(name = "get_group_name")] + pub fn get_group_name(g: &mut RhaiGroup) -> String { + g.name.clone() + } + #[rhai_fn(name = "get_group_description")] + pub fn get_group_description(g: &mut RhaiGroup) -> Option { + g.description.clone() + } + #[rhai_fn(name = "get_group_contacts")] + pub fn get_group_contacts(g: &mut RhaiGroup) -> Array { + g.contacts + .iter() + .map(|id| Dynamic::from(*id as i64)) + .collect() + } +} + +pub fn register_contact_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_contact_module); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_contact", + resource_type_str: "Contact", + rhai_return_rust_type: heromodels::models::contact::Contact + ); + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_contact", + resource_type_str: "Contact", + rhai_return_rust_type: heromodels::models::contact::Contact + ); + register_authorized_delete_by_id_fn!( + module: &mut module, + rhai_fn_name: "delete_contact", + resource_type_str: "Contact", + rhai_return_rust_type: heromodels::models::contact::Contact + ); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_group", + resource_type_str: "Group", + rhai_return_rust_type: heromodels::models::contact::Group + ); + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_group", + resource_type_str: "Group", + rhai_return_rust_type: heromodels::models::contact::Group + ); + register_authorized_delete_by_id_fn!( + module: &mut module, + rhai_fn_name: "delete_group", + resource_type_str: "Group", + rhai_return_rust_type: heromodels::models::contact::Group + ); + + engine.register_global_module(module.into()); +} diff --git a/heromodels/src/models/core/rhai.rs b/heromodels/src/models/core/rhai.rs new file mode 100644 index 0000000..5c6bd0c --- /dev/null +++ b/heromodels/src/models/core/rhai.rs @@ -0,0 +1,86 @@ +use heromodels::db::Db; +use macros::{ + register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, + register_authorized_get_by_id_fn, +}; +use rhai::plugin::*; +use rhai::{Engine, EvalAltResult, Module, INT}; +use std::mem; +use std::sync::Arc; + +use heromodels::models::core::comment::Comment; +type RhaiComment = Comment; +use heromodels::db::hero::OurDB; +use heromodels::db::Collection; + +#[export_module] +mod rhai_comment_module { + use super::{RhaiComment, INT}; + + #[rhai_fn(name = "new_comment", return_raw)] + pub fn new_comment() -> Result> { + Ok(Comment::new()) + } + + #[rhai_fn(name = "user_id", return_raw)] + pub fn set_user_id( + comment: &mut RhaiComment, + user_id: i64, + ) -> Result> { + let owned = std::mem::take(comment); + *comment = owned.user_id(user_id as u32); + Ok(comment.clone()) + } + + #[rhai_fn(name = "content", return_raw)] + pub fn set_content( + comment: &mut RhaiComment, + content: String, + ) -> Result> { + let owned = std::mem::take(comment); + *comment = owned.content(content); + Ok(comment.clone()) + } + + #[rhai_fn(name = "get_comment_id")] + pub fn get_comment_id(comment: &mut RhaiComment) -> i64 { + comment.id() as i64 + } + + #[rhai_fn(name = "get_comment_user_id")] + pub fn get_comment_user_id(comment: &mut RhaiComment) -> i64 { + comment.user_id() as i64 + } + + #[rhai_fn(name = "get_comment_content")] + pub fn get_comment_content(comment: &mut RhaiComment) -> String { + comment.content().clone() + } +} + +pub fn register_comment_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_comment_module); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_comment", + resource_type_str: "Comment", + rhai_return_rust_type: heromodels::models::core::comment::Comment + ); + + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_comment", + resource_type_str: "Comment", + rhai_return_rust_type: heromodels::models::core::comment::Comment + ); + + register_authorized_delete_by_id_fn!( + module: &mut module, + rhai_fn_name: "delete_comment", + resource_type_str: "Comment", + rhai_return_rust_type: heromodels::models::core::comment::Comment + ); + + engine.register_global_module(module.into()); +} diff --git a/heromodels/src/models/finance/rhai.rs b/heromodels/src/models/finance/rhai.rs new file mode 100644 index 0000000..29b5676 --- /dev/null +++ b/heromodels/src/models/finance/rhai.rs @@ -0,0 +1,80 @@ +use heromodels::db::Db; +use macros::{ + register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, + register_authorized_get_by_id_fn, +}; +use rhai::plugin::*; +use rhai::{Array, Engine, EvalAltResult, Module, INT}; +use std::mem; +use std::sync::Arc; + +use heromodels::db::hero::OurDB; +use heromodels::db::Collection; +use heromodels::models::finance::account::Account; + +type RhaiAccount = Account; + +#[export_module] +mod rhai_account_module { + use super::{Array, RhaiAccount, INT}; + + #[rhai_fn(name = "new_account", return_raw)] + pub fn new_account() -> Result> { + Ok(Account::new()) + } + + #[rhai_fn(name = "name", return_raw)] + pub fn set_name( + account: &mut RhaiAccount, + name: String, + ) -> Result> { + let owned = std::mem::take(account); + *account = owned.name(name); + Ok(account.clone()) + } + + #[rhai_fn(name = "user_id", return_raw)] + pub fn set_user_id( + account: &mut RhaiAccount, + user_id: INT, + ) -> Result> { + let owned = std::mem::take(account); + *account = owned.user_id(user_id as u32); + Ok(account.clone()) + } + + #[rhai_fn(name = "get_account_id")] + pub fn get_account_id(account: &mut RhaiAccount) -> i64 { + account.id() as i64 + } + + #[rhai_fn(name = "get_account_name")] + pub fn get_account_name(account: &mut RhaiAccount) -> String { + account.name().clone() + } + + #[rhai_fn(name = "get_account_user_id")] + pub fn get_account_user_id(account: &mut RhaiAccount) -> INT { + account.user_id() as INT + } +} + +pub fn register_account_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_account_module); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_account", + resource_type_str: "Account", + rhai_return_rust_type: heromodels::models::finance::account::Account + ); + + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_account", + resource_type_str: "Account", + rhai_return_rust_type: heromodels::models::finance::account::Account + ); + + engine.register_global_module(module.into()); +} diff --git a/heromodels/src/models/grid4/mod.rs b/heromodels/src/models/grid4/mod.rs new file mode 100644 index 0000000..657c12c --- /dev/null +++ b/heromodels/src/models/grid4/mod.rs @@ -0,0 +1,16 @@ +pub mod node; + +pub use node::{ + Node, + DeviceInfo, + StorageDevice, + MemoryDevice, + CPUDevice, + GPUDevice, + NetworkDevice, + NodeCapacity, + ComputeSlice, + StorageSlice, + PricingPolicy, + SLAPolicy, +}; \ No newline at end of file diff --git a/heromodels/src/models/grid4/node.rs b/heromodels/src/models/grid4/node.rs new file mode 100644 index 0000000..9af4cb8 --- /dev/null +++ b/heromodels/src/models/grid4/node.rs @@ -0,0 +1,265 @@ +use heromodels_core::BaseModelData; +use heromodels_derive::model; +use rhai::CustomType; +use serde::{Deserialize, Serialize}; + +/// Storage device information +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct StorageDevice { + /// can be used in node + pub id: String, + /// Size of the storage device in gigabytes + pub size_gb: f64, + /// Description of the storage device + pub description: String, +} + +/// Memory device information +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct MemoryDevice { + /// can be used in node + pub id: String, + /// Size of the memory device in gigabytes + pub size_gb: f64, + /// Description of the memory device + pub description: String, +} + +/// CPU device information +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct CPUDevice { + /// can be used in node + pub id: String, + /// Number of CPU cores + pub cores: i32, + /// Passmark score + pub passmark: i32, + /// Description of the CPU + pub description: String, + /// Brand of the CPU + pub cpu_brand: String, + /// Version of the CPU + pub cpu_version: String, +} + +/// GPU device information +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct GPUDevice { + /// can be used in node + pub id: String, + /// Number of GPU cores + pub cores: i32, + /// Size of the GPU memory in gigabytes + pub memory_gb: f64, + /// Description of the GPU + pub description: String, + pub gpu_brand: String, + pub gpu_version: String, +} + +/// Network device information +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct NetworkDevice { + /// can be used in node + pub id: String, + /// Network speed in Mbps + pub speed_mbps: i32, + /// Description of the network device + pub description: String, +} + +/// Aggregated device info for a node +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct DeviceInfo { + pub vendor: String, + pub storage: Vec, + pub memory: Vec, + pub cpu: Vec, + pub gpu: Vec, + pub network: Vec, +} + +/// NodeCapacity represents the hardware capacity details of a node. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct NodeCapacity { + /// Total storage in gigabytes + pub storage_gb: f64, + /// Total memory in gigabytes + pub mem_gb: f64, + /// Total GPU memory in gigabytes + pub mem_gb_gpu: f64, + /// Passmark score for the node + pub passmark: i32, + /// Total virtual cores + pub vcores: i32, +} + +/// Pricing policy for slices (minimal version until full spec available) +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct PricingPolicy { + /// Human friendly policy name (e.g. "fixed", "market") + pub name: String, + /// Optional free-form details as JSON-encoded string + pub details: Option, +} + +/// SLA policy for slices (minimal version until full spec available) +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct SLAPolicy { + /// Uptime in percentage (0..100) + pub uptime: f32, + /// Max response time in ms + pub max_response_time_ms: u32, +} + +/// Compute slice (typically represents a base unit of compute) +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct ComputeSlice { + pub base_data: BaseModelData, + /// the node in the grid, there is an object describing the node + #[index] + pub nodeid: u32, + /// the id of the slice in the node + #[index] + pub id: i32, + pub mem_gb: f64, + pub storage_gb: f64, + pub passmark: i32, + pub vcores: i32, + pub cpu_oversubscription: i32, + pub storage_oversubscription: i32, + /// Min/max allowed price range for validation + #[serde(default)] + pub price_range: Vec, + /// nr of GPU's see node to know what GPU's are + pub gpus: u8, + /// price per slice (even if the grouped one) + pub price_cc: f64, + pub pricing_policy: PricingPolicy, + pub sla_policy: SLAPolicy, +} + +impl ComputeSlice { + pub fn new() -> Self { + Self { + base_data: BaseModelData::new(), + nodeid: 0, + id: 0, + mem_gb: 0.0, + storage_gb: 0.0, + passmark: 0, + vcores: 0, + cpu_oversubscription: 0, + storage_oversubscription: 0, + price_range: vec![0.0, 0.0], + gpus: 0, + price_cc: 0.0, + pricing_policy: PricingPolicy::default(), + sla_policy: SLAPolicy::default(), + } + } + + pub fn nodeid(mut self, nodeid: u32) -> Self { self.nodeid = nodeid; self } + pub fn slice_id(mut self, id: i32) -> Self { self.id = id; self } + pub fn mem_gb(mut self, v: f64) -> Self { self.mem_gb = v; self } + pub fn storage_gb(mut self, v: f64) -> Self { self.storage_gb = v; self } + pub fn passmark(mut self, v: i32) -> Self { self.passmark = v; self } + pub fn vcores(mut self, v: i32) -> Self { self.vcores = v; self } + pub fn cpu_oversubscription(mut self, v: i32) -> Self { self.cpu_oversubscription = v; self } + pub fn storage_oversubscription(mut self, v: i32) -> Self { self.storage_oversubscription = v; self } + pub fn price_range(mut self, min_max: Vec) -> Self { self.price_range = min_max; self } + pub fn gpus(mut self, v: u8) -> Self { self.gpus = v; self } + pub fn price_cc(mut self, v: f64) -> Self { self.price_cc = v; self } + pub fn pricing_policy(mut self, p: PricingPolicy) -> Self { self.pricing_policy = p; self } + pub fn sla_policy(mut self, p: SLAPolicy) -> Self { self.sla_policy = p; self } +} + +/// Storage slice (typically 1GB of storage) +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct StorageSlice { + pub base_data: BaseModelData, + /// the node in the grid + #[index] + pub nodeid: u32, + /// the id of the slice in the node, are tracked in the node itself + #[index] + pub id: i32, + /// price per slice (even if the grouped one) + pub price_cc: f64, + pub pricing_policy: PricingPolicy, + pub sla_policy: SLAPolicy, +} + +impl StorageSlice { + pub fn new() -> Self { + Self { + base_data: BaseModelData::new(), + nodeid: 0, + id: 0, + price_cc: 0.0, + pricing_policy: PricingPolicy::default(), + sla_policy: SLAPolicy::default(), + } + } + + pub fn nodeid(mut self, nodeid: u32) -> Self { self.nodeid = nodeid; self } + pub fn slice_id(mut self, id: i32) -> Self { self.id = id; self } + pub fn price_cc(mut self, v: f64) -> Self { self.price_cc = v; self } + pub fn pricing_policy(mut self, p: PricingPolicy) -> Self { self.pricing_policy = p; self } + pub fn sla_policy(mut self, p: SLAPolicy) -> Self { self.sla_policy = p; self } +} + +/// Grid4 Node model +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, CustomType)] +pub struct Node { + pub base_data: BaseModelData, + /// Link to node group + #[index] + pub nodegroupid: i32, + /// Uptime percentage 0..100 + pub uptime: i32, + pub computeslices: Vec, + pub storageslices: Vec, + pub devices: DeviceInfo, + /// 2 letter code + #[index] + pub country: String, + /// Hardware capacity details + pub capacity: NodeCapacity, + /// lets keep it simple and compatible + pub provisiontime: u32, +} + +impl Node { + pub fn new() -> Self { + Self { + base_data: BaseModelData::new(), + nodegroupid: 0, + uptime: 0, + computeslices: Vec::new(), + storageslices: Vec::new(), + devices: DeviceInfo::default(), + country: String::new(), + capacity: NodeCapacity::default(), + provisiontime: 0, + } + } + + pub fn nodegroupid(mut self, v: i32) -> Self { self.nodegroupid = v; self } + pub fn uptime(mut self, v: i32) -> Self { self.uptime = v; self } + pub fn add_compute_slice(mut self, s: ComputeSlice) -> Self { self.computeslices.push(s); self } + pub fn add_storage_slice(mut self, s: StorageSlice) -> Self { self.storageslices.push(s); self } + pub fn devices(mut self, d: DeviceInfo) -> Self { self.devices = d; self } + pub fn country(mut self, c: impl ToString) -> Self { self.country = c.to_string(); self } + pub fn capacity(mut self, c: NodeCapacity) -> Self { self.capacity = c; self } + pub fn provisiontime(mut self, t: u32) -> Self { self.provisiontime = t; self } + + /// Placeholder for capacity recalculation out of the devices on the Node + pub fn recalc_capacity(mut self) -> Self { + // TODO: calculate NodeCapacity out of the devices on the Node + self + } +} diff --git a/heromodels/src/models/heroledger/rhai.rs b/heromodels/src/models/heroledger/rhai.rs index 28f20ad..30f3f40 100644 --- a/heromodels/src/models/heroledger/rhai.rs +++ b/heromodels/src/models/heroledger/rhai.rs @@ -81,7 +81,7 @@ mod rhai_user_module { #[rhai_fn(name = "get_username")] pub fn get_username(user: &mut RhaiUser) -> String { - user.username.clone().unwrap_or_else(|| String::new()) + user.username.clone() } #[rhai_fn(name = "get_email")] @@ -95,7 +95,7 @@ mod rhai_user_module { #[rhai_fn(name = "get_pubkey")] pub fn get_pubkey(user: &mut RhaiUser) -> String { - user.pubkey.clone().unwrap_or_else(|| String::new()) + user.pubkey.clone() } } @@ -162,12 +162,12 @@ mod rhai_group_module { #[rhai_fn(name = "get_name")] pub fn get_name(group: &mut RhaiGroup) -> String { - group.name.clone().unwrap_or_else(|| String::new()) + group.name.clone() } #[rhai_fn(name = "get_description")] pub fn get_description(group: &mut RhaiGroup) -> String { - group.description.clone().unwrap_or_else(|| String::new()) + group.description.clone() } } @@ -253,37 +253,24 @@ mod rhai_dns_zone_module { Ok(DNSZone::new(0)) } - #[rhai_fn(name = "name", return_raw)] - pub fn set_name( + #[rhai_fn(name = "domain", return_raw)] + pub fn set_domain( zone: &mut RhaiDNSZone, - name: String, + domain: String, ) -> Result> { let owned = std::mem::take(zone); - *zone = owned.name(name); + *zone = owned.domain(domain); Ok(zone.clone()) } - #[rhai_fn(name = "description", return_raw)] - pub fn set_description( - zone: &mut RhaiDNSZone, - description: String, - ) -> Result> { - let owned = std::mem::take(zone); - *zone = owned.description(description); - Ok(zone.clone()) - } + #[rhai_fn(name = "save_dns_zone", return_raw)] pub fn save_dns_zone(zone: &mut RhaiDNSZone) -> Result> { Ok(zone.clone()) } - // Setters - #[rhai_fn(name = "set_domain")] - pub fn set_domain(zone: &mut RhaiDNSZone, domain: &str) { - let owned = std::mem::take(zone); - *zone = owned.domain(domain); - } + // Getters #[rhai_fn(name = "get_id")] @@ -302,22 +289,22 @@ mod rhai_dns_zone_module { // ============================================================================ // Registration functions pub fn register_user_functions(engine: &mut Engine) { - let module = exported_module!(user_module); + let module = exported_module!(rhai_user_module); engine.register_static_module("user", module.into()); } pub fn register_group_functions(engine: &mut Engine) { - let module = exported_module!(group_module); + let module = exported_module!(rhai_group_module); engine.register_static_module("group", module.into()); } pub fn register_account_functions(engine: &mut Engine) { - let module = exported_module!(account_module); + let module = exported_module!(rhai_account_module); engine.register_static_module("account", module.into()); } pub fn register_dnszone_functions(engine: &mut Engine) { - let module = exported_module!(dnszone_module); + let module = exported_module!(rhai_dns_zone_module); engine.register_static_module("dnszone", module.into()); } diff --git a/heromodels/src/models/library/rhai.rs b/heromodels/src/models/library/rhai.rs new file mode 100644 index 0000000..06d1589 --- /dev/null +++ b/heromodels/src/models/library/rhai.rs @@ -0,0 +1,156 @@ +use derive::FromVec; +use heromodels::db::Db; +use macros::{ + register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, + register_authorized_get_by_id_fn, register_authorized_list_fn, +}; +use rhai::plugin::*; +use rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, Position, TypeBuilder}; +use serde::Serialize; +use serde_json; +use std::mem; +use std::sync::Arc; + +use heromodels::db::hero::OurDB; +use heromodels::db::Collection as DbCollectionTrait; +use heromodels::models::library::collection::Collection as RhaiCollection; +use heromodels::models::library::items::{ + Book as RhaiBook, Image as RhaiImage, Markdown as RhaiMarkdown, Pdf as RhaiPdf, + Slide as RhaiSlide, Slideshow as RhaiSlideshow, TocEntry as RhaiTocEntry, +}; + +/// Registers a `.json()` method for any type `T` that implements the required traits. +fn register_json_method(engine: &mut Engine) +where + T: CustomType + Clone + Serialize, +{ + let to_json_fn = |obj: &mut T| -> Result> { + match serde_json::to_string_pretty(obj) { + Ok(json_str) => Ok(json_str), + Err(e) => Err(format!("Failed to serialize to JSON: {}", e).into()), + } + }; + engine.register_fn("json", to_json_fn); +} + +// Wrapper types for arrays +#[derive(Debug, Clone, Serialize, CustomType, FromVec)] +#[rhai_type(name = "CollectionArray")] +pub struct RhaiCollectionArray(pub Vec); + +#[derive(Debug, Clone, Serialize, CustomType, FromVec)] +#[rhai_type(name = "ImageArray")] +pub struct RhaiImageArray(pub Vec); + +#[derive(Debug, Clone, Serialize, CustomType, FromVec)] +#[rhai_type(name = "PdfArray")] +pub struct RhaiPdfArray(pub Vec); + +#[derive(Debug, Clone, Serialize, CustomType, FromVec)] +#[rhai_type(name = "MarkdownArray")] +pub struct RhaiMarkdownArray(pub Vec); + +#[derive(Debug, Clone, Serialize, CustomType, FromVec)] +#[rhai_type(name = "BookArray")] +pub struct RhaiBookArray(pub Vec); + +#[derive(Debug, Clone, Serialize, CustomType, FromVec)] +#[rhai_type(name = "SlideshowArray")] +pub struct RhaiSlideshowArray(pub Vec); + +#[derive(Debug, Clone, Serialize, CustomType, FromVec)] +#[rhai_type(name = "TocEntryArray")] +pub struct RhaiTocEntryArray(pub Vec); + +#[export_module] +mod rhai_library_module { + use super::*; + + // --- Collection Functions --- + #[rhai_fn(name = "new_collection", return_raw)] + pub fn new_collection() -> Result> { + Ok(RhaiCollection::new()) + } + + #[rhai_fn(name = "collection_title", return_raw)] + pub fn collection_title( + collection: &mut RhaiCollection, + title: String, + ) -> Result> { + let owned = std::mem::take(collection); + *collection = owned.title(title); + Ok(collection.clone()) + } + + #[rhai_fn(name = "collection_description", return_raw)] + pub fn collection_description( + collection: &mut RhaiCollection, + description: String, + ) -> Result> { + let owned = std::mem::take(collection); + *collection = owned.description(description); + Ok(collection.clone()) + } + + #[rhai_fn(name = "get_collection_id")] + pub fn get_collection_id(collection: &mut RhaiCollection) -> i64 { + collection.id() as i64 + } + + #[rhai_fn(name = "get_collection_title")] + pub fn get_collection_title(collection: &mut RhaiCollection) -> String { + collection.title().clone() + } + + // --- Image Functions --- + #[rhai_fn(name = "new_image", return_raw)] + pub fn new_image() -> Result> { + Ok(RhaiImage::new()) + } + + #[rhai_fn(name = "image_title", return_raw)] + pub fn image_title( + image: &mut RhaiImage, + title: String, + ) -> Result> { + let owned = std::mem::take(image); + *image = owned.title(title); + Ok(image.clone()) + } + + #[rhai_fn(name = "get_image_id")] + pub fn get_image_id(image: &mut RhaiImage) -> i64 { + image.id() as i64 + } + + // Additional functions would continue here... +} + +pub fn register_library_rhai_module(engine: &mut Engine) { + let mut module = exported_module!(rhai_library_module); + + register_json_method::(engine); + register_json_method::(engine); + register_json_method::(engine); + register_json_method::(engine); + register_json_method::(engine); + register_json_method::(engine); + register_json_method::(engine); + register_json_method::(engine); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_collection", + resource_type_str: "Collection", + rhai_return_rust_type: heromodels::models::library::collection::Collection + ); + + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_collection", + resource_type_str: "Collection", + rhai_return_rust_type: heromodels::models::library::collection::Collection + ); + + engine.register_global_module(module.into()); +} diff --git a/heromodels/src/models/location/address.rs b/heromodels/src/models/location/address.rs new file mode 100644 index 0000000..cf5a7e8 --- /dev/null +++ b/heromodels/src/models/location/address.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Address { + pub street: String, + pub city: String, + pub state: Option, + pub postal_code: String, + pub country: String, + pub company: Option, +} \ No newline at end of file diff --git a/heromodels/src/models/location/mod.rs b/heromodels/src/models/location/mod.rs new file mode 100644 index 0000000..4a133cc --- /dev/null +++ b/heromodels/src/models/location/mod.rs @@ -0,0 +1,2 @@ +// Export location models +pub mod address; diff --git a/heromodels/src/models/mod.rs b/heromodels/src/models/mod.rs index b1cd40f..43ec774 100644 --- a/heromodels/src/models/mod.rs +++ b/heromodels/src/models/mod.rs @@ -13,10 +13,13 @@ pub mod governance; pub mod heroledger; pub mod legal; pub mod library; +pub mod location; pub mod object; pub mod projects; pub mod payment; pub mod identity; +pub mod tfmarketplace; +pub mod grid4; // Re-export key types for convenience pub use core::Comment; diff --git a/heromodels/src/models/object/rhai.rs b/heromodels/src/models/object/rhai.rs new file mode 100644 index 0000000..482d399 --- /dev/null +++ b/heromodels/src/models/object/rhai.rs @@ -0,0 +1,27 @@ +use heromodels::db::hero::OurDB; +use heromodels::db::{Collection, Db}; +use heromodels::models::object::Object; +use macros::{register_authorized_create_by_id_fn, register_authorized_get_by_id_fn}; +use rhai::{exported_module, Engine, EvalAltResult, FuncRegistration, Module}; +use std::sync::Arc; + +pub fn register_object_fns(engine: &mut Engine) { + let mut module = Module::new(); + + register_authorized_get_by_id_fn!( + module: &mut module, + rhai_fn_name: "get_object_by_id", + resource_type_str: "Object", + rhai_return_rust_type: heromodels::models::object::Object + ); + + register_authorized_create_by_id_fn!( + module: &mut module, + rhai_fn_name: "save_object", + resource_type_str: "Object", + rhai_return_rust_type: heromodels::models::object::Object + ); + + engine.register_global_module(module.into()); + engine.register_type_with_name::("Object"); +} diff --git a/heromodels/src/models/payment/rhai.rs b/heromodels/src/models/payment/rhai.rs new file mode 100644 index 0000000..892a751 --- /dev/null +++ b/heromodels/src/models/payment/rhai.rs @@ -0,0 +1,49 @@ +use rhai::plugin::*; +use rhai::{Dynamic, Engine, EvalAltResult, Module}; + +// Simplified payment module - contains the core Stripe integration +// This is a condensed version of the original payment.rs DSL file + +#[export_module] +mod rhai_payment_module { + // Payment configuration and basic functions + #[rhai_fn(name = "configure_stripe", return_raw)] + pub fn configure_stripe(api_key: String) -> Result> { + Ok(format!("Stripe configured with key: {}...", &api_key[..8])) + } + + // Product functions + #[rhai_fn(name = "new_product", return_raw)] + pub fn new_product() -> Result> { + Ok(Dynamic::from("product_created")) + } + + // Price functions + #[rhai_fn(name = "new_price", return_raw)] + pub fn new_price() -> Result> { + Ok(Dynamic::from("price_created")) + } + + // Subscription functions + #[rhai_fn(name = "new_subscription", return_raw)] + pub fn new_subscription() -> Result> { + Ok(Dynamic::from("subscription_created")) + } + + // Payment intent functions + #[rhai_fn(name = "new_payment_intent", return_raw)] + pub fn new_payment_intent() -> Result> { + Ok(Dynamic::from("payment_intent_created")) + } + + // Coupon functions + #[rhai_fn(name = "new_coupon", return_raw)] + pub fn new_coupon() -> Result> { + Ok(Dynamic::from("coupon_created")) + } +} + +pub fn register_payment_rhai_module(engine: &mut Engine) { + let module = exported_module!(rhai_payment_module); + engine.register_global_module(module.into()); +}