Compare commits
5 Commits
58ed59cd12
...
developmen
Author | SHA1 | Date | |
---|---|---|---|
|
6569e819ae | ||
|
130822b69b | ||
|
7439980b33 | ||
|
cedea2f305 | ||
|
453e86edd2 |
274
Cargo.lock
generated
274
Cargo.lock
generated
@@ -17,6 +17,17 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
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]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.8.12"
|
version = "0.8.12"
|
||||||
@@ -60,7 +71,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -116,6 +127,18 @@ version = "2.9.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
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]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@@ -125,12 +148,57 @@ dependencies = [
|
|||||||
"generic-array",
|
"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]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
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]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -158,6 +226,12 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.41"
|
version = "0.4.41"
|
||||||
@@ -268,6 +342,12 @@ version = "0.2.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55"
|
checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "funty"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -292,7 +372,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -361,6 +441,15 @@ version = "0.31.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
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]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.4"
|
version = "0.15.4"
|
||||||
@@ -387,6 +476,8 @@ dependencies = [
|
|||||||
"r2d2",
|
"r2d2",
|
||||||
"r2d2_postgres",
|
"r2d2_postgres",
|
||||||
"rhai",
|
"rhai",
|
||||||
|
"rhailib-macros",
|
||||||
|
"rust_decimal",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"strum",
|
"strum",
|
||||||
@@ -403,7 +494,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -454,7 +545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.15.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -506,7 +597,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -804,6 +895,15 @@ dependencies = [
|
|||||||
"zerocopy",
|
"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]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.95"
|
||||||
@@ -813,6 +913,26 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
@@ -849,6 +969,12 @@ dependencies = [
|
|||||||
"r2d2",
|
"r2d2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radium"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@@ -917,13 +1043,22 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rend"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
|
||||||
|
dependencies = [
|
||||||
|
"bytecheck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "1.22.2"
|
version = "1.22.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2780e813b755850e50b178931aaf94ed24f6817f46aaaf5d21c13c12d939a249"
|
checksum = "2780e813b755850e50b178931aaf94ed24f6817f46aaaf5d21c13c12d939a249"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash 0.8.12",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"instant",
|
"instant",
|
||||||
"no-std-compat",
|
"no-std-compat",
|
||||||
@@ -944,7 +1079,44 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"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]]
|
[[package]]
|
||||||
@@ -954,7 +1126,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d"
|
checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
"borsh",
|
||||||
|
"bytes",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"rkyv",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -990,6 +1168,12 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seahash"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -1007,7 +1191,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1049,6 +1233,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simdutf8"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -1137,7 +1327,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1146,6 +1336,17 @@ version = "2.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
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]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.104"
|
version = "2.0.104"
|
||||||
@@ -1157,6 +1358,12 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tap"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thin-vec"
|
name = "thin-vec"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@@ -1180,7 +1387,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1235,7 +1442,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1277,6 +1484,23 @@ dependencies = [
|
|||||||
"tokio",
|
"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]]
|
[[package]]
|
||||||
name = "tst"
|
name = "tst"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1390,7 +1614,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1412,7 +1636,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@@ -1468,7 +1692,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1479,7 +1703,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1588,6 +1812,15 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen-rt"
|
name = "wit-bindgen-rt"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
@@ -1597,6 +1830,15 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wyz"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||||
|
dependencies = [
|
||||||
|
"tap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.26"
|
version = "0.8.26"
|
||||||
@@ -1614,5 +1856,5 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
48
heromodels/Cargo.lock
generated
48
heromodels/Cargo.lock
generated
@@ -60,7 +60,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -233,14 +233,6 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -300,7 +292,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -387,7 +379,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
"derive",
|
|
||||||
"heromodels-derive",
|
"heromodels-derive",
|
||||||
"heromodels_core",
|
"heromodels_core",
|
||||||
"jsonb",
|
"jsonb",
|
||||||
@@ -411,7 +402,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -514,7 +505,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -952,7 +943,7 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1015,7 +1006,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1145,7 +1136,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1154,17 +1145,6 @@ version = "2.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
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]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.104"
|
version = "2.0.104"
|
||||||
@@ -1199,7 +1179,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1254,7 +1234,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1409,7 +1389,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1431,7 +1411,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@@ -1487,7 +1467,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1498,7 +1478,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1633,5 +1613,5 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn",
|
||||||
]
|
]
|
||||||
|
@@ -14,12 +14,14 @@ ourdb = { path = "../../herolib_rust/packages/data/ourdb" }
|
|||||||
tst = { path = "../../herolib_rust/packages/data/tst" }
|
tst = { path = "../../herolib_rust/packages/data/tst" }
|
||||||
heromodels-derive = { path = "../heromodels-derive" }
|
heromodels-derive = { path = "../heromodels-derive" }
|
||||||
heromodels_core = { path = "../heromodels_core" }
|
heromodels_core = { path = "../heromodels_core" }
|
||||||
|
rhailib-macros = { path = "../../herolib_rust/rhailib/src/macros" }
|
||||||
rhai = { version = "1.21.0", features = [
|
rhai = { version = "1.21.0", features = [
|
||||||
"std",
|
"std",
|
||||||
"sync",
|
"sync",
|
||||||
"decimal",
|
"decimal",
|
||||||
"internals",
|
"internals",
|
||||||
] } # Added "decimal" feature, sync for Arc<Mutex<>>
|
] } # Added "decimal" feature, sync for Arc<Mutex<>>
|
||||||
|
rust_decimal = { version = "1.36", features = ["serde"] }
|
||||||
strum = "0.26"
|
strum = "0.26"
|
||||||
strum_macros = "0.26"
|
strum_macros = "0.26"
|
||||||
uuid = { version = "1.17.0", features = ["v4"] }
|
uuid = { version = "1.17.0", features = ["v4"] }
|
||||||
|
@@ -73,7 +73,7 @@ fn main() {
|
|||||||
|
|
||||||
// The `#[model]` derive handles `created_at` and `updated_at` in `base_data`.
|
// 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.
|
// `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.
|
// If not, one might call `contract.base_data.touch()` after building.
|
||||||
|
|
||||||
println!("\n--- Initial Contract Details ---");
|
println!("\n--- Initial Contract Details ---");
|
||||||
|
148
heromodels/src/models/access/rhai.rs
Normal file
148
heromodels/src/models/access/rhai.rs
Normal file
@@ -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<RhaiAccess, Box<EvalAltResult>> {
|
||||||
|
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<RhaiAccess, Box<EvalAltResult>> {
|
||||||
|
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<RhaiAccess, Box<EvalAltResult>> {
|
||||||
|
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<RhaiAccess, Box<EvalAltResult>> {
|
||||||
|
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<RhaiAccess, Box<EvalAltResult>> {
|
||||||
|
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<RhaiAccess, Box<EvalAltResult>> {
|
||||||
|
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());
|
||||||
|
}
|
422
heromodels/src/models/biz/rhai.rs
Normal file
422
heromodels/src/models/biz/rhai.rs
Normal file
@@ -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<RhaiProductComponent, Box<EvalAltResult>> {
|
||||||
|
Ok(ProductComponent::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "name", return_raw)]
|
||||||
|
pub fn set_name(
|
||||||
|
component: &mut RhaiProductComponent,
|
||||||
|
name: String,
|
||||||
|
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProductComponent, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProductComponent, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
Ok(Product::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Setters ---
|
||||||
|
#[rhai_fn(name = "name", return_raw)]
|
||||||
|
pub fn set_name(
|
||||||
|
product: &mut RhaiProduct,
|
||||||
|
name: String,
|
||||||
|
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
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<RhaiProduct, Box<EvalAltResult>> {
|
||||||
|
let mut product_components = Vec::new();
|
||||||
|
for component_dynamic in components {
|
||||||
|
if let Ok(component) = component_dynamic.try_cast::<RhaiProductComponent>() {
|
||||||
|
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<RhaiCompany, Box<EvalAltResult>> {
|
||||||
|
Ok(Company::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "name", return_raw)]
|
||||||
|
pub fn set_name(
|
||||||
|
company: &mut RhaiCompany,
|
||||||
|
name: String,
|
||||||
|
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||||
|
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<RhaiSale, Box<EvalAltResult>> {
|
||||||
|
Ok(Sale::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "new_sale_item", return_raw)]
|
||||||
|
pub fn new_sale_item() -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||||
|
Ok(SaleItem::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "company_id", return_raw)]
|
||||||
|
pub fn set_sale_company_id(sale: &mut RhaiSale, company_id: i64) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||||
|
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<RhaiSale, Box<EvalAltResult>> {
|
||||||
|
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());
|
||||||
|
}
|
246
heromodels/src/models/calendar/rhai.rs
Normal file
246
heromodels/src/models/calendar/rhai.rs
Normal file
@@ -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<RhaiAttendee, Box<EvalAltResult>> {
|
||||||
|
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<RhaiAttendee, Box<EvalAltResult>> {
|
||||||
|
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<RhaiEvent, Box<EvalAltResult>> {
|
||||||
|
Ok(Event::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "title", return_raw)]
|
||||||
|
pub fn set_event_title(
|
||||||
|
event: &mut RhaiEvent,
|
||||||
|
title: String,
|
||||||
|
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||||
|
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<RhaiEvent, Box<EvalAltResult>> {
|
||||||
|
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<RhaiEvent, Box<EvalAltResult>> {
|
||||||
|
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<RhaiEvent, Box<EvalAltResult>> {
|
||||||
|
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<RhaiEvent, Box<EvalAltResult>> {
|
||||||
|
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<RhaiCalendar, Box<EvalAltResult>> {
|
||||||
|
Ok(Calendar::new().name(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "calendar_name", return_raw)]
|
||||||
|
pub fn set_calendar_name(
|
||||||
|
calendar: &mut RhaiCalendar,
|
||||||
|
name: String,
|
||||||
|
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||||
|
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<RhaiCalendar, Box<EvalAltResult>> {
|
||||||
|
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<RhaiCalendar, Box<EvalAltResult>> {
|
||||||
|
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<String> {
|
||||||
|
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<String> {
|
||||||
|
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<String> {
|
||||||
|
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());
|
||||||
|
}
|
@@ -1,412 +1,155 @@
|
|||||||
use crate::db::Db;
|
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::plugin::*;
|
||||||
use rhai::{Array, CustomType, Dynamic, Engine, EvalAltResult, INT, Module, Position};
|
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map, Module};
|
||||||
use std::mem;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::circle::{Circle, ThemeData};
|
use crate::models::circle::Circle;
|
||||||
type RhaiCircle = Circle;
|
type RhaiCircle = Circle;
|
||||||
type RhaiThemeData = ThemeData;
|
|
||||||
|
|
||||||
use crate::db::Collection;
|
|
||||||
use crate::db::hero::OurDB;
|
use crate::db::hero::OurDB;
|
||||||
use serde::Serialize;
|
use crate::db::Collection;
|
||||||
use serde_json;
|
use crate::models::circle::ThemeData;
|
||||||
|
|
||||||
/// Registers a `.json()` method for any type `T` that implements the required traits.
|
|
||||||
fn register_json_method<T>(engine: &mut Engine)
|
|
||||||
where
|
|
||||||
T: CustomType + Clone + Serialize,
|
|
||||||
{
|
|
||||||
let to_json_fn = |obj: &mut T| -> Result<String, Box<EvalAltResult>> {
|
|
||||||
serde_json::to_string(obj).map_err(|e| e.to_string().into())
|
|
||||||
};
|
|
||||||
engine.build_type::<T>().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, Box<EvalAltResult>> {
|
|
||||||
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<RhaiThemeData, Box<EvalAltResult>> {
|
|
||||||
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<RhaiThemeData, Box<EvalAltResult>> {
|
|
||||||
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<RhaiThemeData, Box<EvalAltResult>> {
|
|
||||||
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<RhaiThemeData, Box<EvalAltResult>> {
|
|
||||||
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<RhaiThemeData, Box<EvalAltResult>> {
|
|
||||||
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<RhaiThemeData, Box<EvalAltResult>> {
|
|
||||||
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<RhaiThemeData, Box<EvalAltResult>> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod rhai_circle_module {
|
mod rhai_circle_module {
|
||||||
// --- Circle Functions ---
|
use super::RhaiCircle;
|
||||||
#[rhai_fn(name = "new_circle")]
|
|
||||||
pub fn new_circle() -> RhaiCircle {
|
// this one configures the users own circle
|
||||||
Circle::new()
|
#[rhai_fn(name = "configure", return_raw)]
|
||||||
|
pub fn configure() -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
|
Ok(Circle::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the circle title
|
#[rhai_fn(name = "new_circle", return_raw)]
|
||||||
#[rhai_fn(name = "title", return_raw, global, pure)]
|
pub fn new_circle() -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
pub fn circle_title(
|
Ok(Circle::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "set_title", return_raw)]
|
||||||
|
pub fn set_title(
|
||||||
circle: &mut RhaiCircle,
|
circle: &mut RhaiCircle,
|
||||||
title: String,
|
title: String,
|
||||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
let owned_circle = mem::take(circle);
|
let owned = std::mem::take(circle);
|
||||||
*circle = owned_circle.title(title);
|
*circle = owned.title(title);
|
||||||
Ok(circle.clone())
|
Ok(circle.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the circle ws_url
|
#[rhai_fn(name = "set_ws_url", return_raw)]
|
||||||
#[rhai_fn(name = "ws_url", return_raw, global, pure)]
|
pub fn set_ws_url(
|
||||||
pub fn circle_ws_url(
|
|
||||||
circle: &mut RhaiCircle,
|
circle: &mut RhaiCircle,
|
||||||
ws_url: String,
|
ws_url: String,
|
||||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
let owned_circle = mem::take(circle);
|
let owned = std::mem::take(circle);
|
||||||
*circle = owned_circle.ws_url(ws_url);
|
*circle = owned.ws_url(ws_url);
|
||||||
Ok(circle.clone())
|
Ok(circle.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the circle description
|
#[rhai_fn(name = "set_description", return_raw)]
|
||||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
pub fn set_description(
|
||||||
pub fn circle_description(
|
|
||||||
circle: &mut RhaiCircle,
|
circle: &mut RhaiCircle,
|
||||||
description: String,
|
description: String,
|
||||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
let owned_circle = mem::take(circle);
|
let owned = std::mem::take(circle);
|
||||||
*circle = owned_circle.description(description);
|
*circle = owned.description(description);
|
||||||
Ok(circle.clone())
|
Ok(circle.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the circle logo
|
#[rhai_fn(name = "set_logo", return_raw)]
|
||||||
#[rhai_fn(name = "logo", return_raw, global, pure)]
|
pub fn set_logo(
|
||||||
pub fn circle_logo(
|
|
||||||
circle: &mut RhaiCircle,
|
circle: &mut RhaiCircle,
|
||||||
logo: String,
|
logo: String,
|
||||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
let owned_circle = mem::take(circle);
|
let owned = std::mem::take(circle);
|
||||||
*circle = owned_circle.logo(logo);
|
*circle = owned.logo(logo);
|
||||||
Ok(circle.clone())
|
Ok(circle.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the circle theme
|
#[rhai_fn(name = "set_theme", return_raw)]
|
||||||
#[rhai_fn(name = "theme", return_raw, global, pure)]
|
pub fn set_theme(
|
||||||
pub fn circle_theme(
|
|
||||||
circle: &mut RhaiCircle,
|
circle: &mut RhaiCircle,
|
||||||
theme: RhaiThemeData,
|
theme: ThemeData,
|
||||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
let owned_circle = mem::take(circle);
|
let owned = std::mem::take(circle);
|
||||||
*circle = owned_circle.theme(theme);
|
*circle = owned.theme(theme);
|
||||||
Ok(circle.clone())
|
Ok(circle.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an attendee to the circle
|
#[rhai_fn(name = "add_circle", return_raw)]
|
||||||
#[rhai_fn(name = "add_circle", return_raw, global, pure)]
|
pub fn add_circle(
|
||||||
pub fn circle_add_circle(
|
|
||||||
circle: &mut RhaiCircle,
|
circle: &mut RhaiCircle,
|
||||||
added_circle: String,
|
new_circle: String,
|
||||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
let owned_circle = mem::take(circle);
|
let owned = std::mem::take(circle);
|
||||||
*circle = owned_circle.add_circle(added_circle);
|
*circle = owned.add_circle(new_circle);
|
||||||
Ok(circle.clone())
|
Ok(circle.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an attendee to the circle
|
#[rhai_fn(name = "add_member", return_raw)]
|
||||||
#[rhai_fn(name = "add_member", return_raw, global, pure)]
|
pub fn add_member(
|
||||||
pub fn circle_add_member(
|
|
||||||
circle: &mut RhaiCircle,
|
circle: &mut RhaiCircle,
|
||||||
added_member: String,
|
member: String,
|
||||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||||
let owned_circle = mem::take(circle);
|
let owned = std::mem::take(circle);
|
||||||
*circle = owned_circle.add_member(added_member);
|
*circle = owned.add_member(member);
|
||||||
Ok(circle.clone())
|
Ok(circle.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Circle Getters
|
// --- Getters ---
|
||||||
#[rhai_fn(name = "get_id", pure)]
|
#[rhai_fn(name = "get_id")]
|
||||||
pub fn get_circle_id(circle: &mut RhaiCircle) -> i64 {
|
pub fn get_id(c: &mut RhaiCircle) -> i64 {
|
||||||
circle.base_data.id as i64
|
c.base_data.id as i64
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "get_created_at", pure)]
|
#[rhai_fn(name = "get_title")]
|
||||||
pub fn get_circle_created_at(circle: &mut RhaiCircle) -> i64 {
|
pub fn get_title(c: &mut RhaiCircle) -> String {
|
||||||
circle.base_data.created_at
|
c.title.clone()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "get_modified_at", pure)]
|
#[rhai_fn(name = "get_ws_url")]
|
||||||
pub fn get_circle_modified_at(circle: &mut RhaiCircle) -> i64 {
|
pub fn get_ws_url(c: &mut RhaiCircle) -> String {
|
||||||
circle.base_data.modified_at
|
c.ws_url.clone()
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "get_description")]
|
||||||
#[rhai_fn(name = "get_title", pure)]
|
pub fn get_description(c: &mut RhaiCircle) -> Option<String> {
|
||||||
pub fn get_circle_title(circle: &mut RhaiCircle) -> String {
|
c.description.clone()
|
||||||
circle.title.clone()
|
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "get_description", pure)]
|
#[rhai_fn(name = "get_logo")]
|
||||||
pub fn get_circle_description(circle: &mut RhaiCircle) -> Option<String> {
|
pub fn get_logo(c: &mut RhaiCircle) -> Option<String> {
|
||||||
circle.description.clone()
|
c.logo.clone()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "get_circles", pure)]
|
#[rhai_fn(name = "get_circles")]
|
||||||
pub fn get_circle_circles(circle: &mut RhaiCircle) -> Vec<String> {
|
pub fn get_circles(c: &mut RhaiCircle) -> Array {
|
||||||
circle.circles.clone()
|
c.circles.iter().map(|s| Dynamic::from(s.clone())).collect()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "get_ws_url", pure)]
|
#[rhai_fn(name = "get_members")]
|
||||||
pub fn get_circle_ws_url(circle: &mut RhaiCircle) -> String {
|
pub fn get_members(c: &mut RhaiCircle) -> Array {
|
||||||
circle.ws_url.clone()
|
c.members.iter().map(|s| Dynamic::from(s.clone())).collect()
|
||||||
}
|
|
||||||
#[rhai_fn(name = "get_logo", pure)]
|
|
||||||
pub fn get_circle_logo(circle: &mut RhaiCircle) -> Option<String> {
|
|
||||||
circle.logo.clone()
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "get_theme", pure)]
|
|
||||||
pub fn get_circle_theme(circle: &mut RhaiCircle) -> RhaiThemeData {
|
|
||||||
circle.theme.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_circle_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
pub fn register_circle_rhai_module(engine: &mut Engine) {
|
||||||
engine.build_type::<RhaiCircle>();
|
let mut module = exported_module!(rhai_circle_module);
|
||||||
engine.build_type::<RhaiThemeData>();
|
|
||||||
|
|
||||||
let mut db_module = Module::new();
|
register_authorized_create_by_id_fn!(
|
||||||
let circle_module = exported_module!(rhai_circle_module);
|
module: &mut module,
|
||||||
let theme_data_module = exported_module!(rhai_theme_data_module);
|
rhai_fn_name: "save_circle",
|
||||||
|
resource_type_str: "Circle",
|
||||||
engine.register_global_module(circle_module.into());
|
rhai_return_rust_type: crate::models::circle::Circle
|
||||||
engine.register_global_module(theme_data_module.into());
|
);
|
||||||
|
register_authorized_get_by_id_fn!(
|
||||||
register_json_method::<Circle>(engine);
|
module: &mut module,
|
||||||
register_json_method::<ThemeData>(engine);
|
rhai_fn_name: "get_circle",
|
||||||
|
resource_type_str: "Circle",
|
||||||
// Manually register database functions as they need to capture 'db'
|
rhai_return_rust_type: crate::models::circle::Circle
|
||||||
let db_clone_set_circle = db.clone();
|
);
|
||||||
db_module.set_native_fn(
|
register_authorized_delete_by_id_fn!(
|
||||||
"save_circle",
|
module: &mut module,
|
||||||
move |circle: Circle| -> Result<Circle, Box<EvalAltResult>> {
|
rhai_fn_name: "delete_circle",
|
||||||
let result = db_clone_set_circle.set(&circle).map_err(|e| {
|
resource_type_str: "Circle",
|
||||||
Box::new(EvalAltResult::ErrorRuntime(
|
rhai_return_rust_type: crate::models::circle::Circle
|
||||||
format!("DB Error set_circle: {}", e).into(),
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
Ok(result.1)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let db_clone_delete_circle = db.clone();
|
engine.register_global_module(module.into());
|
||||||
db_module.set_native_fn(
|
|
||||||
"delete_circle",
|
|
||||||
move |circle: Circle| -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let result = db_clone_delete_circle
|
|
||||||
.collection::<Circle>()
|
|
||||||
.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<Circle, Box<EvalAltResult>> {
|
|
||||||
let all_circles: Vec<Circle> = 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<RhaiCircle, Box<EvalAltResult>> {
|
|
||||||
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<Circle, Box<EvalAltResult>> {
|
|
||||||
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<Dynamic, Box<EvalAltResult>> {
|
|
||||||
let collection = db_clone_list_circles.collection::<Circle>().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.");
|
|
||||||
}
|
}
|
||||||
|
232
heromodels/src/models/contact/rhai.rs
Normal file
232
heromodels/src/models/contact/rhai.rs
Normal file
@@ -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<RhaiContact, Box<EvalAltResult>> {
|
||||||
|
Ok(Contact::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "name", return_raw)]
|
||||||
|
pub fn set_contact_name(
|
||||||
|
contact: &mut RhaiContact,
|
||||||
|
name: String,
|
||||||
|
) -> Result<RhaiContact, Box<EvalAltResult>> {
|
||||||
|
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<RhaiContact, Box<EvalAltResult>> {
|
||||||
|
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<RhaiContact, Box<EvalAltResult>> {
|
||||||
|
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<RhaiContact, Box<EvalAltResult>> {
|
||||||
|
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<RhaiContact, Box<EvalAltResult>> {
|
||||||
|
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<RhaiContact, Box<EvalAltResult>> {
|
||||||
|
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<RhaiContact, Box<EvalAltResult>> {
|
||||||
|
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<RhaiGroup, Box<EvalAltResult>> {
|
||||||
|
Ok(Group::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "group_name", return_raw)]
|
||||||
|
pub fn set_group_name(
|
||||||
|
group: &mut RhaiGroup,
|
||||||
|
name: String,
|
||||||
|
) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||||
|
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<RhaiGroup, Box<EvalAltResult>> {
|
||||||
|
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<RhaiGroup, Box<EvalAltResult>> {
|
||||||
|
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<String> {
|
||||||
|
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<String> {
|
||||||
|
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<String> {
|
||||||
|
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());
|
||||||
|
}
|
86
heromodels/src/models/core/rhai.rs
Normal file
86
heromodels/src/models/core/rhai.rs
Normal file
@@ -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<RhaiComment, Box<EvalAltResult>> {
|
||||||
|
Ok(Comment::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "user_id", return_raw)]
|
||||||
|
pub fn set_user_id(
|
||||||
|
comment: &mut RhaiComment,
|
||||||
|
user_id: i64,
|
||||||
|
) -> Result<RhaiComment, Box<EvalAltResult>> {
|
||||||
|
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<RhaiComment, Box<EvalAltResult>> {
|
||||||
|
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());
|
||||||
|
}
|
80
heromodels/src/models/finance/rhai.rs
Normal file
80
heromodels/src/models/finance/rhai.rs
Normal file
@@ -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<RhaiAccount, Box<EvalAltResult>> {
|
||||||
|
Ok(Account::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "name", return_raw)]
|
||||||
|
pub fn set_name(
|
||||||
|
account: &mut RhaiAccount,
|
||||||
|
name: String,
|
||||||
|
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||||
|
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<RhaiAccount, Box<EvalAltResult>> {
|
||||||
|
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());
|
||||||
|
}
|
16
heromodels/src/models/grid4/mod.rs
Normal file
16
heromodels/src/models/grid4/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pub mod node;
|
||||||
|
|
||||||
|
pub use node::{
|
||||||
|
Node,
|
||||||
|
DeviceInfo,
|
||||||
|
StorageDevice,
|
||||||
|
MemoryDevice,
|
||||||
|
CPUDevice,
|
||||||
|
GPUDevice,
|
||||||
|
NetworkDevice,
|
||||||
|
NodeCapacity,
|
||||||
|
ComputeSlice,
|
||||||
|
StorageSlice,
|
||||||
|
PricingPolicy,
|
||||||
|
SLAPolicy,
|
||||||
|
};
|
265
heromodels/src/models/grid4/node.rs
Normal file
265
heromodels/src/models/grid4/node.rs
Normal file
@@ -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<StorageDevice>,
|
||||||
|
pub memory: Vec<MemoryDevice>,
|
||||||
|
pub cpu: Vec<CPUDevice>,
|
||||||
|
pub gpu: Vec<GPUDevice>,
|
||||||
|
pub network: Vec<NetworkDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<f64>,
|
||||||
|
/// 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<f64>) -> 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<ComputeSlice>,
|
||||||
|
pub storageslices: Vec<StorageSlice>,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@@ -81,7 +81,7 @@ mod rhai_user_module {
|
|||||||
|
|
||||||
#[rhai_fn(name = "get_username")]
|
#[rhai_fn(name = "get_username")]
|
||||||
pub fn get_username(user: &mut RhaiUser) -> String {
|
pub fn get_username(user: &mut RhaiUser) -> String {
|
||||||
user.username.clone().unwrap_or_else(|| String::new())
|
user.username.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "get_email")]
|
#[rhai_fn(name = "get_email")]
|
||||||
@@ -95,7 +95,7 @@ mod rhai_user_module {
|
|||||||
|
|
||||||
#[rhai_fn(name = "get_pubkey")]
|
#[rhai_fn(name = "get_pubkey")]
|
||||||
pub fn get_pubkey(user: &mut RhaiUser) -> String {
|
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")]
|
#[rhai_fn(name = "get_name")]
|
||||||
pub fn get_name(group: &mut RhaiGroup) -> String {
|
pub fn get_name(group: &mut RhaiGroup) -> String {
|
||||||
group.name.clone().unwrap_or_else(|| String::new())
|
group.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "get_description")]
|
#[rhai_fn(name = "get_description")]
|
||||||
pub fn get_description(group: &mut RhaiGroup) -> String {
|
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))
|
Ok(DNSZone::new(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "name", return_raw)]
|
#[rhai_fn(name = "domain", return_raw)]
|
||||||
pub fn set_name(
|
pub fn set_domain(
|
||||||
zone: &mut RhaiDNSZone,
|
zone: &mut RhaiDNSZone,
|
||||||
name: String,
|
domain: String,
|
||||||
) -> Result<RhaiDNSZone, Box<EvalAltResult>> {
|
) -> Result<RhaiDNSZone, Box<EvalAltResult>> {
|
||||||
let owned = std::mem::take(zone);
|
let owned = std::mem::take(zone);
|
||||||
*zone = owned.name(name);
|
*zone = owned.domain(domain);
|
||||||
Ok(zone.clone())
|
Ok(zone.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "description", return_raw)]
|
|
||||||
pub fn set_description(
|
|
||||||
zone: &mut RhaiDNSZone,
|
|
||||||
description: String,
|
|
||||||
) -> Result<RhaiDNSZone, Box<EvalAltResult>> {
|
|
||||||
let owned = std::mem::take(zone);
|
|
||||||
*zone = owned.description(description);
|
|
||||||
Ok(zone.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rhai_fn(name = "save_dns_zone", return_raw)]
|
#[rhai_fn(name = "save_dns_zone", return_raw)]
|
||||||
pub fn save_dns_zone(zone: &mut RhaiDNSZone) -> Result<RhaiDNSZone, Box<EvalAltResult>> {
|
pub fn save_dns_zone(zone: &mut RhaiDNSZone) -> Result<RhaiDNSZone, Box<EvalAltResult>> {
|
||||||
Ok(zone.clone())
|
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
|
// Getters
|
||||||
#[rhai_fn(name = "get_id")]
|
#[rhai_fn(name = "get_id")]
|
||||||
@@ -302,22 +289,22 @@ mod rhai_dns_zone_module {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Registration functions
|
// Registration functions
|
||||||
pub fn register_user_functions(engine: &mut Engine) {
|
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());
|
engine.register_static_module("user", module.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_group_functions(engine: &mut Engine) {
|
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());
|
engine.register_static_module("group", module.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_account_functions(engine: &mut Engine) {
|
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());
|
engine.register_static_module("account", module.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_dnszone_functions(engine: &mut Engine) {
|
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());
|
engine.register_static_module("dnszone", module.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
156
heromodels/src/models/library/rhai.rs
Normal file
156
heromodels/src/models/library/rhai.rs
Normal file
@@ -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<T>(engine: &mut Engine)
|
||||||
|
where
|
||||||
|
T: CustomType + Clone + Serialize,
|
||||||
|
{
|
||||||
|
let to_json_fn = |obj: &mut T| -> Result<String, Box<EvalAltResult>> {
|
||||||
|
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<RhaiCollection>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, CustomType, FromVec)]
|
||||||
|
#[rhai_type(name = "ImageArray")]
|
||||||
|
pub struct RhaiImageArray(pub Vec<RhaiImage>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, CustomType, FromVec)]
|
||||||
|
#[rhai_type(name = "PdfArray")]
|
||||||
|
pub struct RhaiPdfArray(pub Vec<RhaiPdf>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, CustomType, FromVec)]
|
||||||
|
#[rhai_type(name = "MarkdownArray")]
|
||||||
|
pub struct RhaiMarkdownArray(pub Vec<RhaiMarkdown>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, CustomType, FromVec)]
|
||||||
|
#[rhai_type(name = "BookArray")]
|
||||||
|
pub struct RhaiBookArray(pub Vec<RhaiBook>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, CustomType, FromVec)]
|
||||||
|
#[rhai_type(name = "SlideshowArray")]
|
||||||
|
pub struct RhaiSlideshowArray(pub Vec<RhaiSlideshow>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, CustomType, FromVec)]
|
||||||
|
#[rhai_type(name = "TocEntryArray")]
|
||||||
|
pub struct RhaiTocEntryArray(pub Vec<RhaiTocEntry>);
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod rhai_library_module {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// --- Collection Functions ---
|
||||||
|
#[rhai_fn(name = "new_collection", return_raw)]
|
||||||
|
pub fn new_collection() -> Result<RhaiCollection, Box<EvalAltResult>> {
|
||||||
|
Ok(RhaiCollection::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "collection_title", return_raw)]
|
||||||
|
pub fn collection_title(
|
||||||
|
collection: &mut RhaiCollection,
|
||||||
|
title: String,
|
||||||
|
) -> Result<RhaiCollection, Box<EvalAltResult>> {
|
||||||
|
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<RhaiCollection, Box<EvalAltResult>> {
|
||||||
|
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<RhaiImage, Box<EvalAltResult>> {
|
||||||
|
Ok(RhaiImage::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "image_title", return_raw)]
|
||||||
|
pub fn image_title(
|
||||||
|
image: &mut RhaiImage,
|
||||||
|
title: String,
|
||||||
|
) -> Result<RhaiImage, Box<EvalAltResult>> {
|
||||||
|
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::<RhaiCollection>(engine);
|
||||||
|
register_json_method::<RhaiImage>(engine);
|
||||||
|
register_json_method::<RhaiPdf>(engine);
|
||||||
|
register_json_method::<RhaiMarkdown>(engine);
|
||||||
|
register_json_method::<RhaiBook>(engine);
|
||||||
|
register_json_method::<RhaiSlideshow>(engine);
|
||||||
|
register_json_method::<RhaiTocEntry>(engine);
|
||||||
|
register_json_method::<RhaiCollectionArray>(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());
|
||||||
|
}
|
11
heromodels/src/models/location/address.rs
Normal file
11
heromodels/src/models/location/address.rs
Normal file
@@ -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<String>,
|
||||||
|
pub postal_code: String,
|
||||||
|
pub country: String,
|
||||||
|
pub company: Option<String>,
|
||||||
|
}
|
2
heromodels/src/models/location/mod.rs
Normal file
2
heromodels/src/models/location/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Export location models
|
||||||
|
pub mod address;
|
@@ -13,10 +13,13 @@ pub mod governance;
|
|||||||
pub mod heroledger;
|
pub mod heroledger;
|
||||||
pub mod legal;
|
pub mod legal;
|
||||||
pub mod library;
|
pub mod library;
|
||||||
|
pub mod location;
|
||||||
pub mod object;
|
pub mod object;
|
||||||
pub mod projects;
|
pub mod projects;
|
||||||
pub mod payment;
|
pub mod payment;
|
||||||
pub mod identity;
|
pub mod identity;
|
||||||
|
pub mod tfmarketplace;
|
||||||
|
pub mod grid4;
|
||||||
|
|
||||||
// Re-export key types for convenience
|
// Re-export key types for convenience
|
||||||
pub use core::Comment;
|
pub use core::Comment;
|
||||||
|
27
heromodels/src/models/object/rhai.rs
Normal file
27
heromodels/src/models/object/rhai.rs
Normal file
@@ -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>("Object");
|
||||||
|
}
|
49
heromodels/src/models/payment/rhai.rs
Normal file
49
heromodels/src/models/payment/rhai.rs
Normal file
@@ -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<String, Box<EvalAltResult>> {
|
||||||
|
Ok(format!("Stripe configured with key: {}...", &api_key[..8]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Product functions
|
||||||
|
#[rhai_fn(name = "new_product", return_raw)]
|
||||||
|
pub fn new_product() -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
Ok(Dynamic::from("product_created"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Price functions
|
||||||
|
#[rhai_fn(name = "new_price", return_raw)]
|
||||||
|
pub fn new_price() -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
Ok(Dynamic::from("price_created"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscription functions
|
||||||
|
#[rhai_fn(name = "new_subscription", return_raw)]
|
||||||
|
pub fn new_subscription() -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
Ok(Dynamic::from("subscription_created"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payment intent functions
|
||||||
|
#[rhai_fn(name = "new_payment_intent", return_raw)]
|
||||||
|
pub fn new_payment_intent() -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
Ok(Dynamic::from("payment_intent_created"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coupon functions
|
||||||
|
#[rhai_fn(name = "new_coupon", return_raw)]
|
||||||
|
pub fn new_coupon() -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
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());
|
||||||
|
}
|
115
heromodels/src/models/tfmarketplace/activity.rs
Normal file
115
heromodels/src/models/tfmarketplace/activity.rs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use crate::models::tfmarketplace::user::ResourceUtilization;
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct UserActivityBuilder {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
activity_type: Option<crate::models::user::ActivityType>,
|
||||||
|
description: Option<String>,
|
||||||
|
timestamp: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
|
metadata: Option<std::collections::HashMap<String, serde_json::Value>>,
|
||||||
|
category: Option<String>,
|
||||||
|
importance: Option<crate::models::user::ActivityImportance>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserActivityBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(mut self) -> Self{
|
||||||
|
self.base_data.id = Some(id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activity_type(mut self, activity_type: crate::models::user::ActivityType) -> Self {
|
||||||
|
self.activity_type = Some(activity_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(mut self, description: impl Into<String>) -> Self {
|
||||||
|
self.description = Some(description.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timestamp(mut self, timestamp: chrono::DateTime<chrono::Utc>) -> Self {
|
||||||
|
self.timestamp = Some(timestamp);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(mut self, metadata: std::collections::HashMap<String, serde_json::Value>) -> Self {
|
||||||
|
self.metadata = Some(metadata);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn category(mut self, category: impl Into<String>) -> Self {
|
||||||
|
self.category = Some(category.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn importance(mut self, importance: crate::models::user::ActivityImportance) -> Self {
|
||||||
|
self.importance = Some(importance);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<crate::models::user::UserActivity, String> {
|
||||||
|
Ok(crate::models::user::UserActivity {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: self.base_data.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()) - moved to base_data,
|
||||||
|
activity_type: self.activity_type.ok_or("activity_type is required")?,
|
||||||
|
description: self.description.unwrap_or_else(|| "No description".to_string()),
|
||||||
|
timestamp: self.timestamp.unwrap_or_else(|| chrono::Utc::now()),
|
||||||
|
metadata: self.metadata.unwrap_or_default(),
|
||||||
|
category: self.category.unwrap_or_else(|| "General".to_string()),
|
||||||
|
importance: self.importance.unwrap_or(crate::models::user::ActivityImportance::Medium),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// User Activity Tracking
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct UserActivity {
|
||||||
|
|
||||||
|
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
pub activity_type: ActivityType,
|
||||||
|
pub description: String,
|
||||||
|
#[serde(deserialize_with = "deserialize_datetime")]
|
||||||
|
pub timestamp: DateTime<Utc>,
|
||||||
|
pub metadata: std::collections::HashMap<String, serde_json::Value>,
|
||||||
|
pub category: String,
|
||||||
|
pub importance: ActivityImportance,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum ActivityType {
|
||||||
|
Login,
|
||||||
|
Purchase,
|
||||||
|
Deployment,
|
||||||
|
ServiceCreated,
|
||||||
|
AppPublished,
|
||||||
|
NodeAdded,
|
||||||
|
NodeUpdated,
|
||||||
|
WalletTransaction,
|
||||||
|
ProfileUpdate,
|
||||||
|
SettingsChange,
|
||||||
|
MarketplaceView,
|
||||||
|
SliceCreated,
|
||||||
|
SliceAllocated,
|
||||||
|
SliceReleased,
|
||||||
|
SliceRentalStarted,
|
||||||
|
SliceRentalStopped,
|
||||||
|
SliceRentalRestarted,
|
||||||
|
SliceRentalCancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum ActivityImportance {
|
||||||
|
Low,
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
Critical,
|
||||||
|
}
|
361
heromodels/src/models/tfmarketplace/app.rs
Normal file
361
heromodels/src/models/tfmarketplace/app.rs
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Unified App struct that can represent published apps, deployments, and deployment stats
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct App {
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
|
||||||
|
// Core app information
|
||||||
|
pub name: String,
|
||||||
|
pub category: Option<String>,
|
||||||
|
pub version: Option<String>,
|
||||||
|
pub status: String,
|
||||||
|
|
||||||
|
// Deployment information
|
||||||
|
pub customer_name: Option<String>,
|
||||||
|
pub customer_email: Option<String>,
|
||||||
|
pub deployed_date: Option<String>,
|
||||||
|
pub health_score: Option<f32>,
|
||||||
|
pub region: Option<String>,
|
||||||
|
pub instances: Option<i32>,
|
||||||
|
pub resource_usage: Option<ResourceUtilization>,
|
||||||
|
|
||||||
|
// Business metrics
|
||||||
|
pub deployments: Option<i32>,
|
||||||
|
pub rating: Option<f32>,
|
||||||
|
pub monthly_revenue_usd: Option<i32>,
|
||||||
|
pub cost_per_month: Option<Decimal>,
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
pub last_updated: Option<String>,
|
||||||
|
pub auto_healing: Option<bool>,
|
||||||
|
pub provider: Option<String>,
|
||||||
|
pub deployed_at: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
/// Convenience method to get the app ID
|
||||||
|
pub fn id(&self) -> &u32 {
|
||||||
|
&self.base_data.id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get category with default
|
||||||
|
pub fn category_or_default(&self) -> String {
|
||||||
|
self.category.clone().unwrap_or_else(|| "Application".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get version with default
|
||||||
|
pub fn version_or_default(&self) -> String {
|
||||||
|
self.version.clone().unwrap_or_else(|| "1.0.0".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get deployments count with default
|
||||||
|
pub fn deployments_or_default(&self) -> i32 {
|
||||||
|
self.deployments.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get rating with default
|
||||||
|
pub fn rating_or_default(&self) -> f32 {
|
||||||
|
self.rating.unwrap_or(4.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get monthly revenue with default
|
||||||
|
pub fn monthly_revenue_usd_or_default(&self) -> i32 {
|
||||||
|
self.monthly_revenue_usd.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get last updated with default
|
||||||
|
pub fn last_updated_or_default(&self) -> String {
|
||||||
|
self.last_updated.clone().unwrap_or_else(|| "Unknown".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get auto healing with default
|
||||||
|
pub fn auto_healing_or_default(&self) -> bool {
|
||||||
|
self.auto_healing.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Deployment {
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
pub app_id: String,
|
||||||
|
pub instance_id: String,
|
||||||
|
pub status: String,
|
||||||
|
pub region: String,
|
||||||
|
pub health_score: Option<f32>,
|
||||||
|
pub resource_usage: Option<ResourceUtilization>,
|
||||||
|
pub deployed_at: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resource utilization information
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
|
pub struct ResourceUtilization {
|
||||||
|
pub cpu: i32,
|
||||||
|
pub memory: i32,
|
||||||
|
pub storage: i32,
|
||||||
|
pub network: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deployment status enumeration
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
|
pub enum DeploymentStatus {
|
||||||
|
#[default]
|
||||||
|
Running,
|
||||||
|
Stopped,
|
||||||
|
Failed,
|
||||||
|
Pending,
|
||||||
|
Maintenance,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unified App builder
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AppBuilder {
|
||||||
|
base_data: BaseModelData,
|
||||||
|
name: Option<String>,
|
||||||
|
category: Option<String>,
|
||||||
|
version: Option<String>,
|
||||||
|
status: Option<String>,
|
||||||
|
customer_name: Option<String>,
|
||||||
|
customer_email: Option<String>,
|
||||||
|
deployed_date: Option<String>,
|
||||||
|
health_score: Option<f32>,
|
||||||
|
region: Option<String>,
|
||||||
|
instances: Option<i32>,
|
||||||
|
resource_usage: Option<ResourceUtilization>,
|
||||||
|
deployments: Option<i32>,
|
||||||
|
rating: Option<f32>,
|
||||||
|
monthly_revenue_usd: Option<i32>,
|
||||||
|
cost_per_month: Option<Decimal>,
|
||||||
|
last_updated: Option<String>,
|
||||||
|
auto_healing: Option<bool>,
|
||||||
|
provider: Option<String>,
|
||||||
|
deployed_at: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(mut self, name: impl Into<String>) -> Self {
|
||||||
|
self.name = Some(name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn category(mut self, category: impl Into<String>) -> Self {
|
||||||
|
self.category = Some(category.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version(mut self, version: impl Into<String>) -> Self {
|
||||||
|
self.version = Some(version.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status(mut self, status: impl Into<String>) -> Self {
|
||||||
|
self.status = Some(status.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn customer_name(mut self, name: impl Into<String>) -> Self {
|
||||||
|
self.customer_name = Some(name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn customer_email(mut self, email: impl Into<String>) -> Self {
|
||||||
|
self.customer_email = Some(email.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deployed_date(mut self, date: impl Into<String>) -> Self {
|
||||||
|
self.deployed_date = Some(date.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn health_score(mut self, score: f32) -> Self {
|
||||||
|
self.health_score = Some(score);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn region(mut self, region: impl Into<String>) -> Self {
|
||||||
|
self.region = Some(region.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instances(mut self, instances: i32) -> Self {
|
||||||
|
self.instances = Some(instances);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resource_usage(mut self, usage: ResourceUtilization) -> Self {
|
||||||
|
self.resource_usage = Some(usage);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deployments(mut self, deployments: i32) -> Self {
|
||||||
|
self.deployments = Some(deployments);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rating(mut self, rating: f32) -> Self {
|
||||||
|
self.rating = Some(rating);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monthly_revenue_usd(mut self, revenue: i32) -> Self {
|
||||||
|
self.monthly_revenue_usd = Some(revenue);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cost_per_month(mut self, cost: Decimal) -> Self {
|
||||||
|
self.cost_per_month = Some(cost);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_updated(mut self, updated: impl Into<String>) -> Self {
|
||||||
|
self.last_updated = Some(updated.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn auto_healing(mut self, enabled: bool) -> Self {
|
||||||
|
self.auto_healing = Some(enabled);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn provider(mut self, provider: impl Into<String>) -> Self {
|
||||||
|
self.provider = Some(provider.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deployed_at(mut self, date: DateTime<Utc>) -> Self {
|
||||||
|
self.deployed_at = Some(date);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<App, String> {
|
||||||
|
Ok(App {
|
||||||
|
base_data: self.base_data,
|
||||||
|
name: self.name.ok_or("name is required")?,
|
||||||
|
category: self.category,
|
||||||
|
version: self.version,
|
||||||
|
status: self.status.unwrap_or_else(|| "Active".to_string()),
|
||||||
|
customer_name: self.customer_name,
|
||||||
|
customer_email: self.customer_email,
|
||||||
|
deployed_date: self.deployed_date,
|
||||||
|
health_score: self.health_score,
|
||||||
|
region: self.region,
|
||||||
|
instances: self.instances,
|
||||||
|
resource_usage: self.resource_usage,
|
||||||
|
deployments: self.deployments,
|
||||||
|
rating: self.rating,
|
||||||
|
monthly_revenue_usd: self.monthly_revenue_usd,
|
||||||
|
cost_per_month: self.cost_per_month,
|
||||||
|
last_updated: self.last_updated,
|
||||||
|
auto_healing: self.auto_healing,
|
||||||
|
provider: self.provider,
|
||||||
|
deployed_at: self.deployed_at,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn builder() -> AppBuilder {
|
||||||
|
AppBuilder::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template methods for common app types
|
||||||
|
pub fn analytics_template(name: &str) -> Self {
|
||||||
|
Self::builder()
|
||||||
|
.name(name)
|
||||||
|
.category("Analytics")
|
||||||
|
.version("1.0.0")
|
||||||
|
.status("Active")
|
||||||
|
.rating(4.5)
|
||||||
|
.auto_healing(true)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn database_template(name: &str) -> Self {
|
||||||
|
Self::builder()
|
||||||
|
.name(name)
|
||||||
|
.category("Database")
|
||||||
|
.version("1.0.0")
|
||||||
|
.status("Active")
|
||||||
|
.rating(4.2)
|
||||||
|
.auto_healing(false) // Databases need manual intervention
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn web_template(name: &str) -> Self {
|
||||||
|
Self::builder()
|
||||||
|
.name(name)
|
||||||
|
.category("Web")
|
||||||
|
.version("1.0.0")
|
||||||
|
.status("Active")
|
||||||
|
.rating(4.0)
|
||||||
|
.auto_healing(true)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fluent methods for chaining
|
||||||
|
pub fn with_stats(mut self, deployments: i32, rating: f32, monthly_revenue_usd: i32) -> Self {
|
||||||
|
self.deployments = Some(deployments);
|
||||||
|
self.rating = Some(rating);
|
||||||
|
self.monthly_revenue_usd = Some(monthly_revenue_usd);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_auto_healing(mut self, enabled: bool) -> Self {
|
||||||
|
self.auto_healing = Some(enabled);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_version(mut self, version: impl Into<String>) -> Self {
|
||||||
|
self.version = Some(version.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_last_updated(mut self, updated: impl Into<String>) -> Self {
|
||||||
|
self.last_updated = Some(updated.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_deployment_info(mut self, customer_name: &str, customer_email: &str, region: &str) -> Self {
|
||||||
|
self.customer_name = Some(customer_name.to_string());
|
||||||
|
self.customer_email = Some(customer_email.to_string());
|
||||||
|
self.region = Some(region.to_string());
|
||||||
|
self.deployed_at = Some(Utc::now());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_resource_usage(mut self, cpu: i32, memory: i32, storage: i32, network: i32) -> Self {
|
||||||
|
self.resource_usage = Some(ResourceUtilization {
|
||||||
|
cpu,
|
||||||
|
memory,
|
||||||
|
storage,
|
||||||
|
network,
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type aliases for backward compatibility
|
||||||
|
pub type PublishedApp = App;
|
||||||
|
pub type AppDeployment = App;
|
||||||
|
pub type DeploymentStat = App;
|
||||||
|
pub type UserDeployment = App;
|
||||||
|
|
||||||
|
pub type PublishedAppBuilder = AppBuilder;
|
||||||
|
pub type AppDeploymentBuilder = AppBuilder;
|
||||||
|
pub type DeploymentStatBuilder = AppBuilder;
|
||||||
|
pub type UserDeploymentBuilder = AppBuilder;
|
351
heromodels/src/models/tfmarketplace/builders.rs
Normal file
351
heromodels/src/models/tfmarketplace/builders.rs
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
//! Builder patterns for all marketplace models
|
||||||
|
//! This module provides a centralized, maintainable way to construct complex structs
|
||||||
|
//! with sensible defaults and validation.
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use rust_decimal_macros::dec;
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
user::{PublishedApp, DeploymentStat, ResourceUtilization, User, UserRole, MockUserData, ServiceBooking},
|
||||||
|
product::{Product, ProductAttribute, ProductAvailability, ProductMetadata},
|
||||||
|
order::{Order, OrderItem, OrderStatus, PaymentDetails, Address, PurchaseType},
|
||||||
|
};
|
||||||
|
use crate::services::user_persistence::AppDeployment;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// USER MODEL BUILDERS
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MockDataBuilder {
|
||||||
|
user_type: Option<String>,
|
||||||
|
include_farmer_data: Option<bool>,
|
||||||
|
include_service_data: Option<bool>,
|
||||||
|
include_app_data: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockDataBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_type(mut self, user_type: impl Into<String>) -> Self {
|
||||||
|
self.user_type = Some(user_type.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn include_farmer_data(mut self, include: bool) -> Self {
|
||||||
|
self.include_farmer_data = Some(include);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn include_service_data(mut self, include: bool) -> Self {
|
||||||
|
self.include_service_data = Some(include);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn include_app_data(mut self, include: bool) -> Self {
|
||||||
|
self.include_app_data = Some(include);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> crate::models::user::MockUserData {
|
||||||
|
// This would create appropriate mock data based on configuration
|
||||||
|
// For now, return a default instance
|
||||||
|
crate::models::user::MockUserData::new_user()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// =============================================================================
|
||||||
|
// FARMER DATA BUILDER
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct FarmerDataBuilder {
|
||||||
|
total_nodes: Option<i32>,
|
||||||
|
online_nodes: Option<i32>,
|
||||||
|
total_capacity: Option<crate::models::user::NodeCapacity>,
|
||||||
|
used_capacity: Option<crate::models::user::NodeCapacity>,
|
||||||
|
monthly_earnings: Option<i32>,
|
||||||
|
total_earnings: Option<i32>,
|
||||||
|
uptime_percentage: Option<f32>,
|
||||||
|
nodes: Option<Vec<crate::models::user::FarmNode>>,
|
||||||
|
earnings_history: Option<Vec<crate::models::user::EarningsRecord>>,
|
||||||
|
active_slices: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FarmerDataBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_nodes(mut self, total_nodes: i32) -> Self {
|
||||||
|
self.total_nodes = Some(total_nodes);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn online_nodes(mut self, online_nodes: i32) -> Self {
|
||||||
|
self.online_nodes = Some(online_nodes);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_capacity(mut self, capacity: crate::models::user::NodeCapacity) -> Self {
|
||||||
|
self.total_capacity = Some(capacity);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn used_capacity(mut self, capacity: crate::models::user::NodeCapacity) -> Self {
|
||||||
|
self.used_capacity = Some(capacity);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monthly_earnings_usd(mut self, earnings: i32) -> Self {
|
||||||
|
self.monthly_earnings = Some(earnings);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_earnings_usd(mut self, earnings: i32) -> Self {
|
||||||
|
self.total_earnings = Some(earnings);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uptime_percentage(mut self, uptime: f32) -> Self {
|
||||||
|
self.uptime_percentage = Some(uptime);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodes(mut self, nodes: Vec<crate::models::user::FarmNode>) -> Self {
|
||||||
|
self.nodes = Some(nodes);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn earnings_history(mut self, history: Vec<crate::models::user::EarningsRecord>) -> Self {
|
||||||
|
self.earnings_history = Some(history);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn earnings(mut self, earnings: Vec<crate::models::user::EarningsRecord>) -> Self {
|
||||||
|
self.earnings_history = Some(earnings);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_slices(mut self, active_slices: i32) -> Self {
|
||||||
|
self.active_slices = Some(active_slices);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_totals(mut self) -> Self {
|
||||||
|
// Calculate totals from existing data
|
||||||
|
if let Some(ref nodes) = self.nodes {
|
||||||
|
self.total_nodes = Some(nodes.len() as i32);
|
||||||
|
self.online_nodes = Some(nodes.iter().filter(|n| matches!(n.status, crate::models::user::NodeStatus::Online)).count() as i32);
|
||||||
|
|
||||||
|
// Calculate total and used capacity from all nodes
|
||||||
|
let mut total_capacity = crate::models::user::NodeCapacity {
|
||||||
|
cpu_cores: 0,
|
||||||
|
memory_gb: 0,
|
||||||
|
storage_gb: 0,
|
||||||
|
bandwidth_mbps: 0,
|
||||||
|
ssd_storage_gb: 0,
|
||||||
|
hdd_storage_gb: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut used_capacity = crate::models::user::NodeCapacity {
|
||||||
|
cpu_cores: 0,
|
||||||
|
memory_gb: 0,
|
||||||
|
storage_gb: 0,
|
||||||
|
bandwidth_mbps: 0,
|
||||||
|
ssd_storage_gb: 0,
|
||||||
|
hdd_storage_gb: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for node in nodes {
|
||||||
|
total_capacity.cpu_cores += node.capacity.cpu_cores;
|
||||||
|
total_capacity.memory_gb += node.capacity.memory_gb;
|
||||||
|
total_capacity.storage_gb += node.capacity.storage_gb;
|
||||||
|
total_capacity.bandwidth_mbps += node.capacity.bandwidth_mbps;
|
||||||
|
total_capacity.ssd_storage_gb += node.capacity.ssd_storage_gb;
|
||||||
|
total_capacity.hdd_storage_gb += node.capacity.hdd_storage_gb;
|
||||||
|
|
||||||
|
used_capacity.cpu_cores += node.used_capacity.cpu_cores;
|
||||||
|
used_capacity.memory_gb += node.used_capacity.memory_gb;
|
||||||
|
used_capacity.storage_gb += node.used_capacity.storage_gb;
|
||||||
|
used_capacity.bandwidth_mbps += node.used_capacity.bandwidth_mbps;
|
||||||
|
used_capacity.ssd_storage_gb += node.used_capacity.ssd_storage_gb;
|
||||||
|
used_capacity.hdd_storage_gb += node.used_capacity.hdd_storage_gb;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.total_capacity = Some(total_capacity);
|
||||||
|
self.used_capacity = Some(used_capacity);
|
||||||
|
|
||||||
|
// Calculate uptime percentage
|
||||||
|
if !nodes.is_empty() {
|
||||||
|
let avg_uptime = nodes.iter().map(|n| n.uptime_percentage).sum::<f32>() / nodes.len() as f32;
|
||||||
|
self.uptime_percentage = Some(avg_uptime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref earnings) = self.earnings_history {
|
||||||
|
let total: i32 = earnings.iter().map(|e| e.amount.to_string().parse::<i32>().unwrap_or(0)).sum();
|
||||||
|
self.total_earnings = Some(total);
|
||||||
|
self.monthly_earnings = Some(total); // Set monthly earnings as well
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<crate::models::user::FarmerData, String> {
|
||||||
|
Ok(crate::models::user::FarmerData {
|
||||||
|
total_nodes: self.total_nodes.unwrap_or(0),
|
||||||
|
online_nodes: self.online_nodes.unwrap_or(0),
|
||||||
|
total_capacity: self.total_capacity.unwrap_or(crate::models::user::NodeCapacity {
|
||||||
|
cpu_cores: 0,
|
||||||
|
memory_gb: 0,
|
||||||
|
storage_gb: 0,
|
||||||
|
bandwidth_mbps: 0,
|
||||||
|
ssd_storage_gb: 0,
|
||||||
|
hdd_storage_gb: 0,
|
||||||
|
}),
|
||||||
|
used_capacity: self.used_capacity.unwrap_or(crate::models::user::NodeCapacity {
|
||||||
|
cpu_cores: 0,
|
||||||
|
memory_gb: 0,
|
||||||
|
storage_gb: 0,
|
||||||
|
bandwidth_mbps: 0,
|
||||||
|
ssd_storage_gb: 0,
|
||||||
|
hdd_storage_gb: 0,
|
||||||
|
}),
|
||||||
|
monthly_earnings_usd: self.monthly_earnings.unwrap_or(0),
|
||||||
|
total_earnings_usd: self.total_earnings.unwrap_or(0),
|
||||||
|
uptime_percentage: self.uptime_percentage.unwrap_or(0.0),
|
||||||
|
nodes: self.nodes.unwrap_or_default(),
|
||||||
|
earnings_history: self.earnings_history.unwrap_or_default(),
|
||||||
|
slice_templates: Vec::default(), // Will be populated separately
|
||||||
|
active_slices: self.active_slices.unwrap_or(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// SERVICE BOOKING BUILDER
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SpendingRecordBuilder {
|
||||||
|
date: Option<String>,
|
||||||
|
amount: Option<i32>,
|
||||||
|
service_name: Option<String>,
|
||||||
|
provider_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpendingRecordBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn date(mut self, date: &str) -> Self {
|
||||||
|
self.date = Some(date.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn amount(mut self, amount: i32) -> Self {
|
||||||
|
self.amount = Some(amount);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_name(mut self, name: &str) -> Self {
|
||||||
|
self.service_name = Some(name.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn provider_name(mut self, name: &str) -> Self {
|
||||||
|
self.provider_name = Some(name.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<crate::models::user::SpendingRecord, String> {
|
||||||
|
Ok(crate::models::user::SpendingRecord {
|
||||||
|
date: self.date.ok_or("Date is required")?,
|
||||||
|
amount: self.amount.unwrap_or(0),
|
||||||
|
service_name: self.service_name.ok_or("Service name is required")?,
|
||||||
|
provider_name: self.provider_name.ok_or("Provider name is required")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::models::user::SpendingRecord {
|
||||||
|
pub fn builder() -> SpendingRecordBuilder {
|
||||||
|
SpendingRecordBuilder::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// AUTO TOP-UP BUILDERS
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AutoTopUpSettingsBuilder {
|
||||||
|
enabled: Option<bool>,
|
||||||
|
threshold_amount: Option<Decimal>,
|
||||||
|
topup_amount: Option<Decimal>,
|
||||||
|
payment_method_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
daily_limit: Option<Decimal>,
|
||||||
|
monthly_limit: Option<Decimal>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoTopUpSettingsBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enabled(mut self, enabled: bool) -> Self {
|
||||||
|
self.enabled = Some(enabled);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn threshold_amount(mut self, amount: Decimal) -> Self {
|
||||||
|
self.threshold_amount = Some(amount);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn topup_amount(mut self, amount: Decimal) -> Self {
|
||||||
|
self.topup_amount = Some(amount);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payment_method_id(mut self) -> Self{
|
||||||
|
self.payment_method_id = Some(id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn daily_limit(mut self, limit: Decimal) -> Self {
|
||||||
|
self.daily_limit = Some(limit);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monthly_limit(mut self, limit: Decimal) -> Self {
|
||||||
|
self.monthly_limit = Some(limit);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<crate::services::user_persistence::AutoTopUpSettings, String> {
|
||||||
|
Ok(crate::services::user_persistence::AutoTopUpSettings {
|
||||||
|
enabled: self.enabled.unwrap_or(false),
|
||||||
|
threshold_amount_usd: self.threshold_amount.unwrap_or(dec!(10.0)),
|
||||||
|
topup_amount_usd: self.topup_amount.unwrap_or(dec!(25.0)),
|
||||||
|
payment_method_base_data: BaseModelData::new(),
|
||||||
|
// id: self.payment_method_id.ok_or("payment_method_id is required")? - moved to base_data,
|
||||||
|
daily_limit_usd: self.daily_limit,
|
||||||
|
monthly_limit_usd: self.monthly_limit,
|
||||||
|
// created_at: chrono::Utc::now() - moved to base_data,
|
||||||
|
// updated_at: chrono::Utc::now() - moved to base_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
105
heromodels/src/models/tfmarketplace/cart.rs
Normal file
105
heromodels/src/models/tfmarketplace/cart.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use crate::models::tfmarketplace::user::ResourceUtilization;
|
||||||
|
|
||||||
|
/// Shopping Cart Models
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CartItem {
|
||||||
|
pub product_id: u32,
|
||||||
|
pub quantity: u32,
|
||||||
|
pub selected_specifications: HashMap<String, serde_json::Value>,
|
||||||
|
pub added_at: DateTime<Utc>,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Cart {
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
pub items: Vec<CartItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cart {
|
||||||
|
pub fn new() -> Self{
|
||||||
|
let now = Utc::now();
|
||||||
|
Self {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
items: Vec::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_item(&mut self, item: CartItem) {
|
||||||
|
// Check if item already exists and update quantity
|
||||||
|
if let Some(existing_item) = self.items.iter_mut()
|
||||||
|
.find(|i| i.product_id == item.product_id && i.selected_specifications == item.selected_specifications) {
|
||||||
|
existing_item.quantity += item.quantity;
|
||||||
|
} else {
|
||||||
|
self.items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_item(&mut self, product_id: &str, name: &str) -> bool{
|
||||||
|
let initial_len = self.items.len();
|
||||||
|
self.items.retain(|item| item.product_id != product_id);
|
||||||
|
if self.items.len() != initial_len {
|
||||||
|
self.base_data.updated_at = Utc::now();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_item_quantity(&mut self, product_id: &str, name: &str) -> bool {
|
||||||
|
if let Some(item) = self.items.iter_mut().find(|i| i.product_id == product_id) {
|
||||||
|
if quantity == 0 {
|
||||||
|
return self.remove_item(product_id);
|
||||||
|
}
|
||||||
|
item.quantity = quantity;
|
||||||
|
item.updated_at = Utc::now();
|
||||||
|
self.base_data.updated_at = Utc::now();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.items.clear();
|
||||||
|
self.base_data.updated_at = Utc::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_total_items(&self) -> u32 {
|
||||||
|
self.items.iter().map(|item| item.quantity).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.items.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CartItem {
|
||||||
|
pub fn new(product_id: &str, name: &str) -> Self {
|
||||||
|
let now = Utc::now();
|
||||||
|
Self {
|
||||||
|
product_id,
|
||||||
|
quantity,
|
||||||
|
selected_specifications: HashMap::default(),
|
||||||
|
added_at: now,
|
||||||
|
// updated_at: now - moved to base_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_specifications(
|
||||||
|
product_id: &str, name: &str) -> Self {
|
||||||
|
let now = Utc::now();
|
||||||
|
Self {
|
||||||
|
product_id,
|
||||||
|
quantity,
|
||||||
|
selected_specifications: specifications,
|
||||||
|
added_at: now,
|
||||||
|
// updated_at: now - moved to base_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
heromodels/src/models/tfmarketplace/currency.rs
Normal file
90
heromodels/src/models/tfmarketplace/currency.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use heromodels_derive::model;
|
||||||
|
use rhai::CustomType;
|
||||||
|
use crate::models::tfmarketplace::user::ResourceUtilization;
|
||||||
|
|
||||||
|
/// Configurable currency support for any currency type
|
||||||
|
#[model]
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||||
|
pub struct Currency {
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
#[index]
|
||||||
|
pub code: String, // USD, EUR, BTC, ETH, etc.
|
||||||
|
pub name: String,
|
||||||
|
pub symbol: String,
|
||||||
|
pub currency_type: CurrencyType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum CurrencyType {
|
||||||
|
Fiat,
|
||||||
|
Cryptocurrency,
|
||||||
|
Token,
|
||||||
|
Points, // For loyalty/reward systems
|
||||||
|
Custom(String), // For marketplace-specific currencies
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Price {
|
||||||
|
pub base_amount: Decimal, // Amount in marketplace base currency
|
||||||
|
pub base_currency: String,
|
||||||
|
pub display_currency: String,
|
||||||
|
pub display_amount: Decimal,
|
||||||
|
pub formatted_display: String,
|
||||||
|
pub conversion_rate: Decimal,
|
||||||
|
pub conversion_timestamp: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Currency {
|
||||||
|
pub fn new(
|
||||||
|
code: String,
|
||||||
|
name: String,
|
||||||
|
symbol: String,
|
||||||
|
currency_type: CurrencyType,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
code,
|
||||||
|
name,
|
||||||
|
symbol,
|
||||||
|
currency_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Price {
|
||||||
|
pub fn new(
|
||||||
|
base_amount: Decimal,
|
||||||
|
base_currency: String,
|
||||||
|
display_currency: String,
|
||||||
|
conversion_rate: Decimal,
|
||||||
|
) -> Self {
|
||||||
|
let display_amount = base_amount * conversion_rate;
|
||||||
|
// Use proper currency symbol formatting - this will be updated by the currency service
|
||||||
|
Self {
|
||||||
|
base_amount,
|
||||||
|
base_currency: base_currency.clone(),
|
||||||
|
display_currency: display_currency.clone(),
|
||||||
|
display_amount,
|
||||||
|
formatted_display: format!("{} {}", display_amount.round_dp(2), display_currency),
|
||||||
|
conversion_rate,
|
||||||
|
conversion_timestamp: Utc::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_with_symbol(&self, symbol: &str) -> String {
|
||||||
|
format!("{} {}",
|
||||||
|
self.display_amount.round_dp(2),
|
||||||
|
symbol
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_formatted_display(&mut self, formatted: String) {
|
||||||
|
self.formatted_display = formatted;
|
||||||
|
}
|
||||||
|
}
|
30
heromodels/src/models/tfmarketplace/farmer.rs
Normal file
30
heromodels/src/models/tfmarketplace/farmer.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
/// Farmer-specific data
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct FarmerData {
|
||||||
|
pub total_nodes: i32,
|
||||||
|
pub online_nodes: i32,
|
||||||
|
pub total_capacity: NodeCapacity,
|
||||||
|
pub used_capacity: NodeCapacity,
|
||||||
|
pub monthly_earnings_usd: i32,
|
||||||
|
pub total_earnings_usd: i32,
|
||||||
|
pub uptime_percentage: f32,
|
||||||
|
pub nodes: Vec<FarmNode>,
|
||||||
|
pub earnings_history: Vec<EarningsRecord>,
|
||||||
|
pub slice_templates: Vec<crate::models::product::Product>,
|
||||||
|
pub active_slices: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct FarmerSettings {
|
||||||
|
#[serde(default)]
|
||||||
|
pub auto_accept_deployments: bool,
|
||||||
|
#[serde(default = "default_maintenance_window")]
|
||||||
|
pub maintenance_window: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub notification_preferences: NotificationSettings,
|
||||||
|
pub minimum_deployment_duration: i32, // hours
|
||||||
|
pub preferred_regions: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub default_slice_customizations: Option<std::collections::HashMap<String, serde_json::Value>>, // Placeholder for DefaultSliceFormat
|
||||||
|
}
|
17
heromodels/src/models/tfmarketplace/mod.rs
Normal file
17
heromodels/src/models/tfmarketplace/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Export models - starting with basic models first
|
||||||
|
// pub mod user;
|
||||||
|
// pub mod product;
|
||||||
|
// pub mod currency;
|
||||||
|
// pub mod order;
|
||||||
|
// pub mod pool;
|
||||||
|
// pub mod builders; // Re-enabled with essential builders only
|
||||||
|
// pub mod cart;
|
||||||
|
// pub mod payment;
|
||||||
|
// pub mod service;
|
||||||
|
// pub mod slice;
|
||||||
|
// pub mod node;
|
||||||
|
pub mod app;
|
||||||
|
|
||||||
|
// Re-export commonly used types for easier access
|
||||||
|
pub use app::{App, PublishedApp, PublishedAppBuilder, ResourceUtilization, AppBuilder, DeploymentStatus};
|
||||||
|
// pub mod node; // Temporarily disabled - has many service dependencies
|
1660
heromodels/src/models/tfmarketplace/node.rs
Normal file
1660
heromodels/src/models/tfmarketplace/node.rs
Normal file
File diff suppressed because it is too large
Load Diff
8
heromodels/src/models/tfmarketplace/notes.md
Normal file
8
heromodels/src/models/tfmarketplace/notes.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Notes
|
||||||
|
|
||||||
|
all id's of base objects are u32
|
||||||
|
Cart is front end specific,
|
||||||
|
currency and exchange rates should be calculated by client
|
||||||
|
stuff such as decomal numbers related to presentation shouldnt be in base models
|
||||||
|
purchase doesnt need to now wether it is instant or cart
|
||||||
|
all base objects contain created_at and updated_at, so not needed to be added to every model
|
402
heromodels/src/models/tfmarketplace/order.rs
Normal file
402
heromodels/src/models/tfmarketplace/order.rs
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use heromodels_derive::model;
|
||||||
|
use rhai::CustomType;
|
||||||
|
use crate::models::tfmarketplace::user::ResourceUtilization;
|
||||||
|
|
||||||
|
#[model]
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||||
|
pub struct Order {
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
#[index]
|
||||||
|
pub user_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
pub items: Vec<OrderItem>,
|
||||||
|
pub subtotal_base: Decimal, // In base currency
|
||||||
|
pub total_base: Decimal, // In base currency
|
||||||
|
pub base_currency: String,
|
||||||
|
pub currency_used: String, // Currency user paid in
|
||||||
|
pub currency_total: Decimal, // Amount in user's currency
|
||||||
|
pub conversion_rate: Decimal, // Rate used for conversion
|
||||||
|
pub status: OrderStatus,
|
||||||
|
pub payment_method: String,
|
||||||
|
pub payment_details: Option<PaymentDetails>,
|
||||||
|
pub billing_address: Option<Address>,
|
||||||
|
pub shipping_address: Option<Address>,
|
||||||
|
pub notes: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct OrderItem {
|
||||||
|
pub product_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
pub product_name: String,
|
||||||
|
pub product_category: String,
|
||||||
|
pub quantity: u32,
|
||||||
|
pub unit_price_base: Decimal, // In base currency
|
||||||
|
pub total_price_base: Decimal, // In base currency
|
||||||
|
pub specifications: HashMap<String, serde_json::Value>,
|
||||||
|
pub provider_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
pub provider_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum OrderStatus {
|
||||||
|
Pending,
|
||||||
|
Confirmed,
|
||||||
|
Processing,
|
||||||
|
Deployed,
|
||||||
|
Completed,
|
||||||
|
Cancelled,
|
||||||
|
Refunded,
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Order summary for display purposes
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct OrderSummary {
|
||||||
|
pub subtotal: Decimal,
|
||||||
|
pub tax: Decimal,
|
||||||
|
pub shipping: Decimal,
|
||||||
|
pub discount: Decimal,
|
||||||
|
pub total: Decimal,
|
||||||
|
pub currency: String,
|
||||||
|
pub item_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Order {
|
||||||
|
pub fn new(
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
user_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
base_currency: String,
|
||||||
|
currency_used: String,
|
||||||
|
conversion_rate: Decimal,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
user_id,
|
||||||
|
items: Vec::default(),
|
||||||
|
subtotal_base: Decimal::from(0),
|
||||||
|
total_base: Decimal::from(0),
|
||||||
|
base_currency,
|
||||||
|
currency_used,
|
||||||
|
currency_total: Decimal::from(0),
|
||||||
|
conversion_rate,
|
||||||
|
status: OrderStatus::Pending,
|
||||||
|
payment_method: String::new(),
|
||||||
|
payment_details: None,
|
||||||
|
billing_address: None,
|
||||||
|
shipping_address: None,
|
||||||
|
notes: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_item(&mut self, item: OrderItem) {
|
||||||
|
self.items.push(item);
|
||||||
|
self.calculate_totals();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_totals(&mut self) {
|
||||||
|
self.subtotal_base = self.items.iter()
|
||||||
|
.map(|item| item.total_price_base)
|
||||||
|
.sum();
|
||||||
|
self.total_base = self.subtotal_base; // Add taxes, fees, etc. here
|
||||||
|
self.currency_total = self.total_base * self.conversion_rate;
|
||||||
|
self.base_data.modified_at = Utc::now().timestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_status(&mut self, status: OrderStatus) {
|
||||||
|
self.status = status;
|
||||||
|
self.base_data.modified_at = Utc::now().timestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_payment_details(&mut self, payment_details: PaymentDetails) {
|
||||||
|
self.payment_details = Some(payment_details);
|
||||||
|
self.base_data.modified_at = Utc::now().timestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_item_count(&self) -> u32 {
|
||||||
|
self.items.iter().map(|item| item.quantity).sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OrderItem {
|
||||||
|
pub fn new(
|
||||||
|
product_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
product_name: String,
|
||||||
|
product_category: String,
|
||||||
|
quantity: u32,
|
||||||
|
unit_price_base: Decimal,
|
||||||
|
provider_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
provider_name: String,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
product_id,
|
||||||
|
product_name,
|
||||||
|
product_category,
|
||||||
|
quantity,
|
||||||
|
unit_price_base,
|
||||||
|
total_price_base: unit_price_base * Decimal::from(quantity),
|
||||||
|
specifications: HashMap::default(),
|
||||||
|
provider_id,
|
||||||
|
provider_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_specification(&mut self, key: String, value: serde_json::Value) {
|
||||||
|
self.specifications.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_quantity(&mut self, quantity: u32) {
|
||||||
|
self.quantity = quantity;
|
||||||
|
self.total_price_base = self.unit_price_base * Decimal::from(quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct OrderBuilder {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
user_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
items: Vec<OrderItem>,
|
||||||
|
subtotal_base: Option<Decimal>,
|
||||||
|
total_base: Option<Decimal>,
|
||||||
|
base_currency: Option<String>,
|
||||||
|
currency_used: Option<String>,
|
||||||
|
currency_total: Option<Decimal>,
|
||||||
|
conversion_rate: Option<Decimal>,
|
||||||
|
status: Option<OrderStatus>,
|
||||||
|
payment_method: Option<String>,
|
||||||
|
payment_details: Option<PaymentDetails>,
|
||||||
|
billing_address: Option<Address>,
|
||||||
|
shipping_address: Option<Address>,
|
||||||
|
notes: Option<String>,
|
||||||
|
purchase_type: Option<PurchaseType>,
|
||||||
|
// created_at: Option<DateTime<Utc>> - moved to base_data,
|
||||||
|
// updated_at: Option<DateTime<Utc>> - moved to base_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OrderBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(mut self) -> Self{
|
||||||
|
self.base_data.id = Some(id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_id(mut self, user_id: &str, name: &str) -> Self{
|
||||||
|
self.user_id = Some(user_id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_item(mut self, item: OrderItem) -> Self {
|
||||||
|
self.items.push(item);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn items(mut self, items: Vec<OrderItem>) -> Self {
|
||||||
|
self.items = items;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subtotal_base(mut self, subtotal: Decimal) -> Self {
|
||||||
|
self.subtotal_base = Some(subtotal);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_base(mut self, total: Decimal) -> Self {
|
||||||
|
self.total_base = Some(total);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_currency(mut self, currency: impl Into<String>) -> Self {
|
||||||
|
self.base_currency = Some(currency.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn currency_used(mut self, currency: impl Into<String>) -> Self {
|
||||||
|
self.currency_used = Some(currency.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn currency_total(mut self, total: Decimal) -> Self {
|
||||||
|
self.currency_total = Some(total);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn conversion_rate(mut self, rate: Decimal) -> Self {
|
||||||
|
self.conversion_rate = Some(rate);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status(mut self, status: OrderStatus) -> Self {
|
||||||
|
self.status = Some(status);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payment_method(mut self, method: impl Into<String>) -> Self {
|
||||||
|
self.payment_method = Some(method.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payment_details(mut self, details: PaymentDetails) -> Self {
|
||||||
|
self.payment_details = Some(details);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn billing_address(mut self, address: Address) -> Self {
|
||||||
|
self.billing_address = Some(address);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shipping_address(mut self, address: Address) -> Self {
|
||||||
|
self.shipping_address = Some(address);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notes(mut self, notes: impl Into<String>) -> Self {
|
||||||
|
self.notes = Some(notes.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn purchase_type(mut self, purchase_type: PurchaseType) -> Self {
|
||||||
|
self.purchase_type = Some(purchase_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<Order, String> {
|
||||||
|
let now = Utc::now();
|
||||||
|
let subtotal = self.subtotal_base.unwrap_or_else(|| {
|
||||||
|
self.items.iter().map(|item| item.total_price_base).sum()
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Order {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: self.base_data.id.ok_or("id is required")? - moved to base_data,
|
||||||
|
user_base_data: BaseModelData::new(),
|
||||||
|
// id: self.user_id.ok_or("user_id is required")? - moved to base_data,
|
||||||
|
items: self.items,
|
||||||
|
subtotal_base: subtotal,
|
||||||
|
total_base: self.total_base.unwrap_or(subtotal),
|
||||||
|
base_currency: self.base_currency.unwrap_or_else(|| "USD".to_string()),
|
||||||
|
currency_used: self.currency_used.unwrap_or_else(|| "USD".to_string()),
|
||||||
|
currency_total: self.currency_total.unwrap_or(subtotal),
|
||||||
|
conversion_rate: self.conversion_rate.unwrap_or_else(|| Decimal::from(1)),
|
||||||
|
status: self.status.unwrap_or(OrderStatus::Pending),
|
||||||
|
payment_method: self.payment_method.unwrap_or_else(|| "credit_card".to_string()),
|
||||||
|
payment_details: self.payment_details,
|
||||||
|
billing_address: self.billing_address,
|
||||||
|
shipping_address: self.shipping_address,
|
||||||
|
notes: self.notes,
|
||||||
|
purchase_type: self.purchase_type.unwrap_or(PurchaseType::Cart),
|
||||||
|
// created_at: self.base_data.created_at.unwrap_or(now) - moved to base_data,
|
||||||
|
// updated_at: self.base_data.updated_at.unwrap_or(now) - moved to base_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Order {
|
||||||
|
pub fn builder() -> OrderBuilder {
|
||||||
|
OrderBuilder::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct OrderItemBuilder {
|
||||||
|
product_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
product_name: Option<String>,
|
||||||
|
product_category: Option<String>,
|
||||||
|
quantity: Option<u32>,
|
||||||
|
unit_price_base: Option<Decimal>,
|
||||||
|
total_price_base: Option<Decimal>,
|
||||||
|
specifications: HashMap<String, Value>,
|
||||||
|
provider_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
provider_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OrderItemBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn product_id(mut self) -> Self{
|
||||||
|
self.product_id = Some(id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn product_name(mut self, name: impl Into<String>) -> Self {
|
||||||
|
self.product_name = Some(name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn product_category(mut self, category: impl Into<String>) -> Self {
|
||||||
|
self.product_category = Some(category.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn quantity(mut self, quantity: u32) -> Self {
|
||||||
|
self.quantity = Some(quantity);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unit_price_base(mut self, price: Decimal) -> Self {
|
||||||
|
self.unit_price_base = Some(price);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_specification(mut self, key: impl Into<String>, value: Value) -> Self {
|
||||||
|
self.specifications.insert(key.into(), value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn provider_id(mut self) -> Self{
|
||||||
|
self.provider_id = Some(id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn provider_name(mut self, name: impl Into<String>) -> Self {
|
||||||
|
self.provider_name = Some(name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<OrderItem, String> {
|
||||||
|
let quantity = self.quantity.unwrap_or(1);
|
||||||
|
let unit_price = self.unit_price_base.ok_or("unit_price_base is required")?;
|
||||||
|
let total_price = self.total_price_base.unwrap_or(unit_price * Decimal::from(quantity));
|
||||||
|
|
||||||
|
Ok(OrderItem {
|
||||||
|
product_base_data: BaseModelData::new(),
|
||||||
|
// id: self.product_id.ok_or("product_id is required")? - moved to base_data,
|
||||||
|
product_name: self.product_name.ok_or("product_name is required")?,
|
||||||
|
product_category: self.product_category.ok_or("product_category is required")?,
|
||||||
|
quantity,
|
||||||
|
unit_price_base: unit_price,
|
||||||
|
total_price_base: total_price,
|
||||||
|
specifications: self.specifications,
|
||||||
|
provider_base_data: BaseModelData::new(),
|
||||||
|
// id: self.provider_id.ok_or("provider_id is required")? - moved to base_data,
|
||||||
|
provider_name: self.provider_name.ok_or("provider_name is required")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OrderItem {
|
||||||
|
pub fn builder() -> OrderItemBuilder {
|
||||||
|
OrderItemBuilder::new()
|
||||||
|
}
|
||||||
|
}
|
77
heromodels/src/models/tfmarketplace/payment.rs
Normal file
77
heromodels/src/models/tfmarketplace/payment.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use crate::models::tfmarketplace::user::ResourceUtilization;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PaymentDetails {
|
||||||
|
pub payment_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
pub payment_method: PaymentMethod,
|
||||||
|
pub transaction_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
pub payment_status: PaymentStatus,
|
||||||
|
pub payment_timestamp: Option<DateTime<Utc>>,
|
||||||
|
pub failure_reason: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum PaymentMethod {
|
||||||
|
CreditCard {
|
||||||
|
last_four: String,
|
||||||
|
card_type: String,
|
||||||
|
},
|
||||||
|
BankTransfer {
|
||||||
|
bank_name: String,
|
||||||
|
account_last_four: String,
|
||||||
|
},
|
||||||
|
Cryptocurrency {
|
||||||
|
currency: String,
|
||||||
|
wallet_address: String,
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: String,
|
||||||
|
wallet_address: String,
|
||||||
|
},
|
||||||
|
Mock {
|
||||||
|
method_name: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum PaymentStatus {
|
||||||
|
Pending,
|
||||||
|
Processing,
|
||||||
|
Completed,
|
||||||
|
Failed,
|
||||||
|
Cancelled,
|
||||||
|
Refunded,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaymentDetails {
|
||||||
|
pub fn new(payment_id: &str, name: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
payment_id,
|
||||||
|
payment_method,
|
||||||
|
transaction_base_data: BaseModelData::new(),
|
||||||
|
// id: None - moved to base_data,
|
||||||
|
payment_status: PaymentStatus::Pending,
|
||||||
|
payment_timestamp: None,
|
||||||
|
failure_reason: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_completed(&mut self, transaction_id: String) { - moved to base_data
|
||||||
|
self.transaction_id = Some(transaction_id);
|
||||||
|
self.payment_status = PaymentStatus::Completed;
|
||||||
|
self.payment_timestamp = Some(Utc::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_failed(&mut self, reason: String) {
|
||||||
|
self.payment_status = PaymentStatus::Failed;
|
||||||
|
self.failure_reason = Some(reason);
|
||||||
|
self.payment_timestamp = Some(Utc::now());
|
||||||
|
}
|
||||||
|
}
|
105
heromodels/src/models/tfmarketplace/pool.rs
Normal file
105
heromodels/src/models/tfmarketplace/pool.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use crate::models::tfmarketplace::user::ResourceUtilization;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct LiquidityPool {
|
||||||
|
|
||||||
|
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
pub name: String,
|
||||||
|
pub token_a: String,
|
||||||
|
pub token_b: String,
|
||||||
|
pub reserve_a: Decimal,
|
||||||
|
pub reserve_b: Decimal,
|
||||||
|
pub exchange_rate: Decimal,
|
||||||
|
pub liquidity: Decimal,
|
||||||
|
pub volume_24h: Decimal,
|
||||||
|
pub fee_percentage: Decimal,
|
||||||
|
pub status: PoolStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum PoolStatus {
|
||||||
|
Active,
|
||||||
|
Paused,
|
||||||
|
Maintenance,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ExchangeRequest {
|
||||||
|
pub pool_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
pub from_token: String,
|
||||||
|
pub to_token: String,
|
||||||
|
pub amount: Decimal,
|
||||||
|
pub min_receive: Option<Decimal>,
|
||||||
|
pub slippage_tolerance: Option<Decimal>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ExchangeResponse {
|
||||||
|
pub success: bool,
|
||||||
|
pub message: String,
|
||||||
|
pub transaction_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
pub from_amount: Option<Decimal>,
|
||||||
|
pub to_amount: Option<Decimal>,
|
||||||
|
pub exchange_rate: Option<Decimal>,
|
||||||
|
pub fee: Option<Decimal>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct StakeRequest {
|
||||||
|
pub amount: Decimal,
|
||||||
|
pub duration_months: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct StakePosition {
|
||||||
|
|
||||||
|
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
pub user_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
pub amount: Decimal,
|
||||||
|
pub start_date: DateTime<Utc>,
|
||||||
|
pub end_date: DateTime<Utc>,
|
||||||
|
pub discount_percentage: Decimal,
|
||||||
|
pub reputation_bonus: i32,
|
||||||
|
pub status: StakeStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum StakeStatus {
|
||||||
|
Active,
|
||||||
|
Completed,
|
||||||
|
Withdrawn,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pool analytics data
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PoolAnalytics {
|
||||||
|
pub price_history: Vec<PricePoint>,
|
||||||
|
pub volume_history: Vec<VolumePoint>,
|
||||||
|
pub liquidity_distribution: HashMap<String, Decimal>,
|
||||||
|
pub staking_distribution: HashMap<String, i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PricePoint {
|
||||||
|
pub timestamp: DateTime<Utc>,
|
||||||
|
pub price: Decimal,
|
||||||
|
pub volume: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct VolumePoint {
|
||||||
|
pub date: String,
|
||||||
|
pub volume: Decimal,
|
||||||
|
}
|
660
heromodels/src/models/tfmarketplace/product.rs
Normal file
660
heromodels/src/models/tfmarketplace/product.rs
Normal file
@@ -0,0 +1,660 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use heromodels_derive::model;
|
||||||
|
use rhai::CustomType;
|
||||||
|
|
||||||
|
/// Generic product structure that can represent any marketplace item
|
||||||
|
#[model]
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||||
|
pub struct Product {
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
#[index]
|
||||||
|
pub name: String,
|
||||||
|
pub category: ProductCategory,
|
||||||
|
pub description: String,
|
||||||
|
pub price: Price,
|
||||||
|
pub attributes: HashMap<String, ProductAttribute>, // Generic attributes
|
||||||
|
pub provider_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
pub provider_name: String,
|
||||||
|
pub availability: ProductAvailability,
|
||||||
|
pub metadata: ProductMetadata, // Extensible metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct Price {
|
||||||
|
pub base_amount: Decimal,
|
||||||
|
pub currency: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configurable product categories
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct ProductCategory {
|
||||||
|
|
||||||
|
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
pub name: String,
|
||||||
|
pub display_name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub attribute_schema: Vec<AttributeDefinition>, // Defines allowed attributes
|
||||||
|
pub parent_category: Option<String>,
|
||||||
|
pub is_active: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic attribute system for any product type
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct ProductAttribute {
|
||||||
|
pub key: String,
|
||||||
|
pub value: serde_json::Value,
|
||||||
|
pub attribute_type: AttributeType,
|
||||||
|
pub is_searchable: bool,
|
||||||
|
pub is_filterable: bool,
|
||||||
|
pub display_order: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum AttributeType {
|
||||||
|
Text,
|
||||||
|
Number,
|
||||||
|
SliceConfiguration,
|
||||||
|
Boolean,
|
||||||
|
Select(Vec<String>), // Predefined options
|
||||||
|
MultiSelect(Vec<String>),
|
||||||
|
Range { min: f64, max: f64 },
|
||||||
|
Custom(String), // For marketplace-specific types
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct AttributeDefinition {
|
||||||
|
pub key: String,
|
||||||
|
pub name: String,
|
||||||
|
pub attribute_type: AttributeType,
|
||||||
|
pub is_required: bool,
|
||||||
|
pub is_searchable: bool,
|
||||||
|
pub is_filterable: bool,
|
||||||
|
pub validation_rules: Vec<ValidationRule>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum ValidationRule {
|
||||||
|
MinLength(usize),
|
||||||
|
MaxLength(usize),
|
||||||
|
MinValue(f64),
|
||||||
|
MaxValue(f64),
|
||||||
|
Pattern(String),
|
||||||
|
Custom(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum ProductAvailability {
|
||||||
|
Available,
|
||||||
|
Limited,
|
||||||
|
Unavailable,
|
||||||
|
PreOrder,
|
||||||
|
Custom(String), // For marketplace-specific availability states
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProductAvailability {
|
||||||
|
fn default() -> Self {
|
||||||
|
ProductAvailability::Available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum ProductVisibility {
|
||||||
|
Public,
|
||||||
|
Private,
|
||||||
|
Draft,
|
||||||
|
Archived,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProductVisibility {
|
||||||
|
fn default() -> Self {
|
||||||
|
ProductVisibility::Public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
pub struct ProductMetadata {
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
pub location: Option<String>,
|
||||||
|
pub rating: Option<f32>,
|
||||||
|
pub review_count: u32,
|
||||||
|
pub featured: bool,
|
||||||
|
pub last_updated: chrono::DateTime<chrono::Utc>,
|
||||||
|
pub visibility: ProductVisibility,
|
||||||
|
pub seo_keywords: Vec<String>,
|
||||||
|
pub custom_fields: HashMap<String, serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Support for different pricing models
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum PricingModel {
|
||||||
|
OneTime, // Single purchase
|
||||||
|
Recurring { interval: String }, // Subscription
|
||||||
|
UsageBased { unit: String }, // Pay per use
|
||||||
|
Tiered(Vec<PriceTier>), // Volume discounts
|
||||||
|
Custom(String), // Marketplace-specific
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PriceTier {
|
||||||
|
pub min_quantity: u32,
|
||||||
|
pub max_quantity: Option<u32>,
|
||||||
|
pub price_per_unit: Decimal,
|
||||||
|
pub discount_percentage: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Product {
|
||||||
|
pub fn new(
|
||||||
|
name: String,
|
||||||
|
category: ProductCategory,
|
||||||
|
description: String,
|
||||||
|
price: Price,
|
||||||
|
provider_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
provider_name: String,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
description,
|
||||||
|
price,
|
||||||
|
attributes: HashMap::default(),
|
||||||
|
provider_id,
|
||||||
|
provider_name,
|
||||||
|
availability: ProductAvailability::Available,
|
||||||
|
metadata: ProductMetadata {
|
||||||
|
tags: Vec::default(),
|
||||||
|
location: None,
|
||||||
|
rating: None,
|
||||||
|
review_count: 0,
|
||||||
|
featured: false,
|
||||||
|
last_updated: chrono::Utc::now(),
|
||||||
|
visibility: ProductVisibility::Public,
|
||||||
|
seo_keywords: Vec::new(),
|
||||||
|
custom_fields: HashMap::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_attribute(&mut self, key: String, value: serde_json::Value, attribute_type: AttributeType) {
|
||||||
|
let attribute = ProductAttribute {
|
||||||
|
key: key.clone(),
|
||||||
|
value,
|
||||||
|
attribute_type,
|
||||||
|
is_searchable: true,
|
||||||
|
is_filterable: true,
|
||||||
|
display_order: None,
|
||||||
|
};
|
||||||
|
self.attributes.insert(key, attribute);
|
||||||
|
self.base_data.modified_at = Utc::now().timestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_featured(&mut self, featured: bool) {
|
||||||
|
self.metadata.featured = featured;
|
||||||
|
self.base_data.modified_at = Utc::now().timestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_tag(&mut self, tag: String) {
|
||||||
|
if !self.metadata.tags.contains(&tag) {
|
||||||
|
self.metadata.tags.push(tag);
|
||||||
|
self.base_data.modified_at = Utc::now().timestamp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_rating(&mut self, rating: f32, review_count: u32) {
|
||||||
|
self.metadata.rating = Some(rating);
|
||||||
|
self.metadata.review_count = review_count;
|
||||||
|
self.base_data.modified_at = Utc::now().timestamp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProductCategory {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
// id: String - moved to base_data, name: String, display_name: String, description: String) -> Self {
|
||||||
|
Self {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
name,
|
||||||
|
display_name,
|
||||||
|
description,
|
||||||
|
attribute_schema: Vec::default(),
|
||||||
|
parent_category: None,
|
||||||
|
is_active: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add attribute definition to category schema
|
||||||
|
pub fn add_attribute_definition(&mut self, definition: AttributeDefinition) {
|
||||||
|
self.attribute_schema.push(definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Product {
|
||||||
|
/// Create a slice product from farmer configuration
|
||||||
|
pub fn create_slice_product(
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
farmer_name: String,
|
||||||
|
slice_name: String,
|
||||||
|
slice_config: SliceConfiguration,
|
||||||
|
price_per_hour: Decimal,
|
||||||
|
) -> Self {
|
||||||
|
let category = ProductCategory {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: "compute_slices".to_string() - moved to base_data,
|
||||||
|
name: "Compute Slices".to_string(),
|
||||||
|
display_name: "Compute Slices".to_string(),
|
||||||
|
description: "Virtual compute resources".to_string(),
|
||||||
|
attribute_schema: Vec::new(),
|
||||||
|
parent_category: None,
|
||||||
|
is_active: true,
|
||||||
|
};
|
||||||
|
let price = Price {
|
||||||
|
base_amount: price_per_hour,
|
||||||
|
currency: 1, // USD currency ID
|
||||||
|
};
|
||||||
|
let mut product = Self::new(
|
||||||
|
base_data,
|
||||||
|
slice_name,
|
||||||
|
category,
|
||||||
|
format!("Compute slice with {} vCPU, {}GB RAM, {}GB storage",
|
||||||
|
slice_config.cpu_cores, slice_config.memory_gb, slice_config.storage_gb),
|
||||||
|
price,
|
||||||
|
farmer_id,
|
||||||
|
farmer_name,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add slice-specific attributes
|
||||||
|
product.add_attribute(
|
||||||
|
"cpu_cores".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(slice_config.cpu_cores)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"memory_gb".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(slice_config.memory_gb)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"storage_gb".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(slice_config.storage_gb)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"bandwidth_mbps".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(slice_config.bandwidth_mbps)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"min_uptime_sla".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from_f64(slice_config.min_uptime_sla as f64).unwrap()),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"public_ips".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(slice_config.public_ips)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(ref node_id) = slice_config.node_id {
|
||||||
|
product.add_attribute(
|
||||||
|
"node_id".to_string(),
|
||||||
|
serde_json::Value::String(node_id.clone()),
|
||||||
|
AttributeType::Text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"slice_type".to_string(),
|
||||||
|
serde_json::Value::String(format!("{:?}", slice_config.slice_type)),
|
||||||
|
AttributeType::Text,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add slice configuration as a complex attribute
|
||||||
|
product.add_attribute(
|
||||||
|
"slice_configuration".to_string(),
|
||||||
|
serde_json::to_value(&slice_config).unwrap(),
|
||||||
|
AttributeType::SliceConfiguration,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add relevant tags
|
||||||
|
product.add_tag("compute".to_string());
|
||||||
|
product.add_tag("slice".to_string());
|
||||||
|
product.add_tag(format!("{:?}", slice_config.slice_type).to_lowercase());
|
||||||
|
|
||||||
|
product
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if this product is a slice
|
||||||
|
pub fn is_slice(&self) -> bool {
|
||||||
|
self.category.id == "compute_slices" ||
|
||||||
|
self.attributes.contains_key("slice_configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get slice configuration from product attributes
|
||||||
|
pub fn get_slice_configuration(&self) -> Option<SliceConfiguration> {
|
||||||
|
self.attributes.get("slice_configuration")
|
||||||
|
.and_then(|attr| serde_json::from_value(attr.value.clone()).ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update slice configuration
|
||||||
|
pub fn update_slice_configuration(&mut self, config: SliceConfiguration) {
|
||||||
|
if self.is_slice() {
|
||||||
|
self.add_attribute(
|
||||||
|
"slice_configuration".to_string(),
|
||||||
|
serde_json::to_value(&config).unwrap(),
|
||||||
|
AttributeType::SliceConfiguration,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update individual attributes for searchability
|
||||||
|
self.add_attribute(
|
||||||
|
"cpu_cores".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(config.cpu_cores)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.add_attribute(
|
||||||
|
"memory_gb".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(config.memory_gb)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.add_attribute(
|
||||||
|
"storage_gb".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(config.storage_gb)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if slice fits within node capacity
|
||||||
|
pub fn slice_fits_in_node(&self, node_capacity: &crate::models::user::NodeCapacity) -> bool {
|
||||||
|
if let Some(config) = self.get_slice_configuration() {
|
||||||
|
config.cpu_cores <= node_capacity.cpu_cores &&
|
||||||
|
config.memory_gb <= node_capacity.memory_gb &&
|
||||||
|
config.storage_gb <= node_capacity.storage_gb &&
|
||||||
|
config.bandwidth_mbps <= node_capacity.bandwidth_mbps
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Create a full node product from a FarmNode
|
||||||
|
pub fn create_full_node_product(
|
||||||
|
node: &crate::models::user::FarmNode,
|
||||||
|
farmer_email: &str,
|
||||||
|
farmer_name: &str,
|
||||||
|
) -> Self {
|
||||||
|
let category = ProductCategory {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: "3nodes".to_string() - moved to base_data,
|
||||||
|
name: "3Nodes".to_string(),
|
||||||
|
display_name: "3Nodes".to_string(),
|
||||||
|
description: "Full node rentals".to_string(),
|
||||||
|
attribute_schema: Vec::new(),
|
||||||
|
parent_category: None,
|
||||||
|
is_active: true,
|
||||||
|
};
|
||||||
|
let price = Price {
|
||||||
|
base_amount: node.rental_options
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|opts| opts.full_node_pricing.as_ref())
|
||||||
|
.map(|pricing| pricing.monthly)
|
||||||
|
.unwrap_or_else(|| Decimal::from(200)), // Default price
|
||||||
|
currency: 1, // USD currency ID
|
||||||
|
};
|
||||||
|
let mut product = Product {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
name: format!("Full Node: {}", node.name),
|
||||||
|
category,
|
||||||
|
description: format!(
|
||||||
|
"Exclusive access to {} with {} CPU cores, {}GB RAM, {}GB storage in {}",
|
||||||
|
node.name, node.capacity.cpu_cores, node.capacity.memory_gb,
|
||||||
|
node.capacity.storage_gb, node.location
|
||||||
|
),
|
||||||
|
price,
|
||||||
|
attributes: HashMap::new(),
|
||||||
|
provider_base_data: BaseModelData::new(),
|
||||||
|
// id: farmer_email.to_string() - moved to base_data,
|
||||||
|
provider_name: farmer_name.to_string(),
|
||||||
|
availability: match node.availability_status {
|
||||||
|
crate::models::user::NodeAvailabilityStatus::Available => ProductAvailability::Available,
|
||||||
|
crate::models::user::NodeAvailabilityStatus::PartiallyRented => ProductAvailability::Limited,
|
||||||
|
_ => ProductAvailability::Unavailable,
|
||||||
|
},
|
||||||
|
metadata: ProductMetadata {
|
||||||
|
tags: vec!["full-node".to_string(), "exclusive".to_string(), node.region.clone()],
|
||||||
|
location: Some(node.location.clone()),
|
||||||
|
rating: None,
|
||||||
|
review_count: 0,
|
||||||
|
featured: false,
|
||||||
|
last_updated: chrono::Utc::now(),
|
||||||
|
visibility: ProductVisibility::Public,
|
||||||
|
seo_keywords: Vec::new(),
|
||||||
|
custom_fields: HashMap::new(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add node-specific attributes
|
||||||
|
product.add_attribute(
|
||||||
|
"node_id".to_string(),
|
||||||
|
serde_json::Value::String(node.id.clone()),
|
||||||
|
AttributeType::Text,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"rental_type".to_string(),
|
||||||
|
serde_json::Value::String("full_node".to_string()),
|
||||||
|
AttributeType::Text,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"cpu_cores".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(node.capacity.cpu_cores)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"memory_gb".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(node.capacity.memory_gb)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"storage_gb".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(node.capacity.storage_gb)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"bandwidth_mbps".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(node.capacity.bandwidth_mbps)),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"location".to_string(),
|
||||||
|
serde_json::Value::String(node.location.clone()),
|
||||||
|
AttributeType::Text,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"uptime_percentage".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from_f64(node.uptime_percentage as f64).unwrap_or_else(|| serde_json::Number::from(0))),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product.add_attribute(
|
||||||
|
"health_score".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from_f64(node.health_score as f64).unwrap_or_else(|| serde_json::Number::from(0))),
|
||||||
|
AttributeType::Number,
|
||||||
|
);
|
||||||
|
|
||||||
|
product
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if this product represents a full node
|
||||||
|
pub fn is_full_node(&self) -> bool {
|
||||||
|
self.attributes.get("rental_type")
|
||||||
|
.and_then(|attr| attr.value.as_str())
|
||||||
|
.map(|s| s == "full_node")
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the node ID if this is a node product
|
||||||
|
pub fn get_node_id(&self) -> Option<String> {
|
||||||
|
self.attributes.get("node_id")
|
||||||
|
.and_then(|attr| attr.value.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProductCategory {
|
||||||
|
pub fn set_parent_category(&mut self, parent_id: String) {
|
||||||
|
self.parent_category = Some(parent_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttributeDefinition {
|
||||||
|
pub fn new(
|
||||||
|
key: String,
|
||||||
|
name: String,
|
||||||
|
attribute_type: AttributeType,
|
||||||
|
is_required: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
key,
|
||||||
|
name,
|
||||||
|
attribute_type,
|
||||||
|
is_required,
|
||||||
|
is_searchable: true,
|
||||||
|
is_filterable: true,
|
||||||
|
validation_rules: Vec::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_validation_rule(&mut self, rule: ValidationRule) {
|
||||||
|
self.validation_rules.push(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ProductBuilder {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
name: Option<String>,
|
||||||
|
category_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
description: Option<String>,
|
||||||
|
base_price: Option<Decimal>,
|
||||||
|
base_currency: Option<String>,
|
||||||
|
attributes: HashMap<String, ProductAttribute>,
|
||||||
|
provider_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
provider_name: Option<String>,
|
||||||
|
availability: Option<ProductAvailability>,
|
||||||
|
metadata: Option<ProductMetadata>,
|
||||||
|
// created_at: Option<DateTime<Utc>> - moved to base_data,
|
||||||
|
// updated_at: Option<DateTime<Utc>> - moved to base_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProductBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(mut self, id: impl Into<String>) -> Self {
|
||||||
|
self.base_data.id = Some(id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(mut self, name: impl Into<String>) -> Self {
|
||||||
|
self.name = Some(name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn category_id(mut self, category_id: impl Into<String>) -> Self {
|
||||||
|
self.category_id = Some(category_id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(mut self, description: impl Into<String>) -> Self {
|
||||||
|
self.description = Some(description.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_price(mut self, price: Decimal) -> Self {
|
||||||
|
self.base_price = Some(price);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_currency(mut self, currency: impl Into<String>) -> Self {
|
||||||
|
self.base_currency = Some(currency.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_attribute(mut self, key: impl Into<String>, attribute: ProductAttribute) -> Self {
|
||||||
|
self.attributes.insert(key.into(), attribute);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn provider_id(mut self, provider_id: impl Into<String>) -> Self {
|
||||||
|
self.provider_id = Some(provider_id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn provider_name(mut self, provider_name: impl Into<String>) -> Self {
|
||||||
|
self.provider_name = Some(provider_name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn availability(mut self, availability: ProductAvailability) -> Self {
|
||||||
|
self.availability = Some(availability);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(mut self, metadata: ProductMetadata) -> Self {
|
||||||
|
self.metadata = Some(metadata);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<Product, String> {
|
||||||
|
let now = Utc::now();
|
||||||
|
Ok(Product {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: self.base_data.id.ok_or("id is required")? - moved to base_data,
|
||||||
|
name: self.name.ok_or("name is required")?,
|
||||||
|
category_base_data: BaseModelData::new(),
|
||||||
|
// id: self.category_id.ok_or("category_id is required")? - moved to base_data,
|
||||||
|
description: self.description.unwrap_or_default(),
|
||||||
|
base_price: self.base_price.ok_or("base_price is required")?,
|
||||||
|
base_currency: self.base_currency.unwrap_or_else(|| "USD".to_string()),
|
||||||
|
attributes: self.attributes,
|
||||||
|
provider_base_data: BaseModelData::new(),
|
||||||
|
// id: self.provider_id.ok_or("provider_id is required")? - moved to base_data,
|
||||||
|
provider_name: self.provider_name.ok_or("provider_name is required")?,
|
||||||
|
availability: self.availability.unwrap_or_default(),
|
||||||
|
metadata: self.metadata.unwrap_or_default(),
|
||||||
|
// created_at: self.base_data.created_at.unwrap_or(now) - moved to base_data,
|
||||||
|
// updated_at: self.base_data.updated_at.unwrap_or(now) - moved to base_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Product {
|
||||||
|
pub fn builder() -> ProductBuilder {
|
||||||
|
ProductBuilder::new()
|
||||||
|
}
|
||||||
|
}
|
297
heromodels/src/models/tfmarketplace/service.rs
Normal file
297
heromodels/src/models/tfmarketplace/service.rs
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize, Deserializer};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use crate::models::tfmarketplace::user::ResourceUtilization;
|
||||||
|
|
||||||
|
/// Service Provider-specific data
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ServiceProviderData {
|
||||||
|
pub active_services: i32,
|
||||||
|
pub total_clients: i32,
|
||||||
|
pub monthly_revenue_usd: i32,
|
||||||
|
pub total_revenue_usd: i32,
|
||||||
|
pub service_rating: f32,
|
||||||
|
pub services: Vec<Service>,
|
||||||
|
pub client_requests: Vec<ServiceRequest>,
|
||||||
|
pub revenue_history: Vec<RevenueRecord>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Service {
|
||||||
|
pub base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data,
|
||||||
|
pub name: String,
|
||||||
|
pub category: String,
|
||||||
|
pub description: String,
|
||||||
|
pub price_per_hour_usd: i32,
|
||||||
|
pub status: String,
|
||||||
|
pub clients: i32,
|
||||||
|
pub rating: f32,
|
||||||
|
pub total_hours: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ServiceRequest {
|
||||||
|
|
||||||
|
|
||||||
|
/// Base model data (includes id, created_at, updated_at)
|
||||||
|
pub base_data: BaseModelData,
|
||||||
|
pub client_name: String,
|
||||||
|
pub service_name: String,
|
||||||
|
pub status: String,
|
||||||
|
pub requested_date: String,
|
||||||
|
pub estimated_hours: i32,
|
||||||
|
pub budget: i32,
|
||||||
|
pub priority: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub progress: Option<i32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub completed_date: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub client_email: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub client_phone: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub created_date: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Service booking record for customers who purchase services
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ServiceBooking {
|
||||||
|
pub base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data, // Same as ServiceRequest.id for cross-reference
|
||||||
|
pub service_base_data: BaseModelData::new(),
|
||||||
|
// id: String - moved to base_data, // Reference to original service
|
||||||
|
pub service_name: String,
|
||||||
|
pub provider_email: String, // Who provides the service
|
||||||
|
pub customer_email: String, // Who booked the service
|
||||||
|
pub budget: i32,
|
||||||
|
pub estimated_hours: i32,
|
||||||
|
pub status: String, // "Pending", "In Progress", "Completed"
|
||||||
|
pub requested_date: String,
|
||||||
|
pub priority: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub booking_date: String, // When customer booked
|
||||||
|
pub client_phone: Option<String>,
|
||||||
|
pub progress: Option<i32>,
|
||||||
|
pub completed_date: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Customer Service-specific data (for users who book services)
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CustomerServiceData {
|
||||||
|
pub active_bookings: i32,
|
||||||
|
pub completed_bookings: i32,
|
||||||
|
pub total_spent: i32,
|
||||||
|
pub monthly_spending: i32,
|
||||||
|
pub average_rating_given: f32,
|
||||||
|
pub service_bookings: Vec<ServiceBooking>,
|
||||||
|
pub favorite_providers: Vec<String>,
|
||||||
|
pub spending_history: Vec<SpendingRecord>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SpendingRecord {
|
||||||
|
pub date: String,
|
||||||
|
pub amount: i32,
|
||||||
|
pub service_name: String,
|
||||||
|
pub provider_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ServiceBookingBuilder {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
service_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
service_name: Option<String>,
|
||||||
|
provider_email: Option<String>,
|
||||||
|
customer_email: Option<String>,
|
||||||
|
budget: Option<i32>,
|
||||||
|
estimated_hours: Option<i32>,
|
||||||
|
status: Option<String>,
|
||||||
|
requested_date: Option<String>,
|
||||||
|
priority: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
booking_date: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceBookingBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(mut self) -> Self{
|
||||||
|
self.base_data.id = Some(id.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_id(mut self, service_id: &str, name: &str) -> Self{
|
||||||
|
self.service_id = Some(service_id.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_name(mut self, service_name: &str) -> Self {
|
||||||
|
self.service_name = Some(service_name.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn provider_email(mut self, provider_email: &str) -> Self {
|
||||||
|
self.provider_email = Some(provider_email.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn customer_email(mut self, customer_email: &str) -> Self {
|
||||||
|
self.customer_email = Some(customer_email.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn budget(mut self, budget: i32) -> Self {
|
||||||
|
self.budget = Some(budget);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn estimated_hours(mut self, hours: i32) -> Self {
|
||||||
|
self.estimated_hours = Some(hours);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status(mut self, status: &str) -> Self {
|
||||||
|
self.status = Some(status.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn requested_date(mut self, date: &str) -> Self {
|
||||||
|
self.requested_date = Some(date.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn priority(mut self, priority: &str) -> Self {
|
||||||
|
self.priority = Some(priority.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(mut self, description: Option<String>) -> Self {
|
||||||
|
self.description = description;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn booking_date(mut self, date: &str) -> Self {
|
||||||
|
self.booking_date = Some(date.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<ServiceBooking, String> {
|
||||||
|
Ok(ServiceBooking {
|
||||||
|
base_data: BaseModelData::new(),
|
||||||
|
// id: self.base_data.id.ok_or("ID is required")? - moved to base_data,
|
||||||
|
service_base_data: BaseModelData::new(),
|
||||||
|
// id: self.service_id.ok_or("Service ID is required")? - moved to base_data,
|
||||||
|
service_name: self.service_name.ok_or("Service name is required")?,
|
||||||
|
provider_email: self.provider_email.ok_or("Provider email is required")?,
|
||||||
|
customer_email: self.customer_email.ok_or("Customer email is required")?,
|
||||||
|
budget: self.budget.unwrap_or(0),
|
||||||
|
estimated_hours: self.estimated_hours.unwrap_or(0),
|
||||||
|
status: self.status.unwrap_or_else(|| "Pending".to_string()),
|
||||||
|
requested_date: self.requested_date.unwrap_or_else(|| chrono::Utc::now().format("%Y-%m-%d").to_string()),
|
||||||
|
priority: self.priority.unwrap_or_else(|| "Medium".to_string()),
|
||||||
|
description: self.description,
|
||||||
|
booking_date: self.booking_date.unwrap_or_else(|| chrono::Utc::now().format("%Y-%m-%d").to_string()),
|
||||||
|
client_phone: None,
|
||||||
|
progress: None,
|
||||||
|
completed_date: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceBooking {
|
||||||
|
pub fn builder() -> ServiceBookingBuilder {
|
||||||
|
ServiceBookingBuilder::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// CUSTOMER SERVICE DATA BUILDER
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CustomerServiceDataBuilder {
|
||||||
|
active_bookings: Option<i32>,
|
||||||
|
completed_bookings: Option<i32>,
|
||||||
|
total_spent: Option<i32>,
|
||||||
|
monthly_spending: Option<i32>,
|
||||||
|
average_rating_given: Option<f32>,
|
||||||
|
service_bookings: Option<Vec<crate::models::user::ServiceBooking>>,
|
||||||
|
favorite_providers: Option<Vec<String>>,
|
||||||
|
spending_history: Option<Vec<crate::models::user::SpendingRecord>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomerServiceDataBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_bookings(mut self, count: i32) -> Self {
|
||||||
|
self.active_bookings = Some(count);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn completed_bookings(mut self, count: i32) -> Self {
|
||||||
|
self.completed_bookings = Some(count);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_spent(mut self, amount: i32) -> Self {
|
||||||
|
self.total_spent = Some(amount);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monthly_spending(mut self, amount: i32) -> Self {
|
||||||
|
self.monthly_spending = Some(amount);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn average_rating_given(mut self, rating: f32) -> Self {
|
||||||
|
self.average_rating_given = Some(rating);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_bookings(mut self, bookings: Vec<crate::models::user::ServiceBooking>) -> Self {
|
||||||
|
self.service_bookings = Some(bookings);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn favorite_providers(mut self, providers: Vec<String>) -> Self {
|
||||||
|
self.favorite_providers = Some(providers);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spending_history(mut self, history: Vec<crate::models::user::SpendingRecord>) -> Self {
|
||||||
|
self.spending_history = Some(history);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<crate::models::user::CustomerServiceData, String> {
|
||||||
|
Ok(crate::models::user::CustomerServiceData {
|
||||||
|
active_bookings: self.active_bookings.unwrap_or(0),
|
||||||
|
completed_bookings: self.completed_bookings.unwrap_or(0),
|
||||||
|
total_spent: self.total_spent.unwrap_or(0),
|
||||||
|
monthly_spending: self.monthly_spending.unwrap_or(0),
|
||||||
|
average_rating_given: self.average_rating_given.unwrap_or(0.0),
|
||||||
|
service_bookings: self.service_bookings.unwrap_or_default(),
|
||||||
|
favorite_providers: self.favorite_providers.unwrap_or_default(),
|
||||||
|
spending_history: self.spending_history.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::models::user::CustomerServiceData {
|
||||||
|
pub fn builder() -> CustomerServiceDataBuilder {
|
||||||
|
CustomerServiceDataBuilder::new()
|
||||||
|
}
|
||||||
|
}
|
200
heromodels/src/models/tfmarketplace/slice.rs
Normal file
200
heromodels/src/models/tfmarketplace/slice.rs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use heromodels_core::BaseModelData;
|
||||||
|
use crate::models::tfmarketplace::user::ResourceUtilization;
|
||||||
|
|
||||||
|
/// Slice configuration data structure for product attributes
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SliceConfiguration {
|
||||||
|
pub cpu_cores: i32,
|
||||||
|
pub memory_gb: i32,
|
||||||
|
pub storage_gb: i32,
|
||||||
|
pub bandwidth_mbps: i32,
|
||||||
|
pub min_uptime_sla: f32,
|
||||||
|
pub public_ips: i32,
|
||||||
|
pub node_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
pub slice_type: SliceType,
|
||||||
|
#[serde(default)]
|
||||||
|
pub pricing: SlicePricing,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enhanced pricing structure for slices with multiple time periods
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SlicePricing {
|
||||||
|
pub hourly: Decimal,
|
||||||
|
pub daily: Decimal,
|
||||||
|
pub monthly: Decimal,
|
||||||
|
pub yearly: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SlicePricing {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hourly: Decimal::ZERO,
|
||||||
|
daily: Decimal::ZERO,
|
||||||
|
monthly: Decimal::ZERO,
|
||||||
|
yearly: Decimal::ZERO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlicePricing {
|
||||||
|
/// Create pricing from hourly rate with automatic calculation
|
||||||
|
pub fn from_hourly(hourly_rate: Decimal, daily_discount: f32, monthly_discount: f32, yearly_discount: f32) -> Self {
|
||||||
|
let base_daily = hourly_rate * Decimal::from(24);
|
||||||
|
let base_monthly = hourly_rate * Decimal::from(24 * 30);
|
||||||
|
let base_yearly = hourly_rate * Decimal::from(24 * 365);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
hourly: hourly_rate,
|
||||||
|
daily: base_daily * Decimal::try_from(1.0 - daily_discount / 100.0).unwrap_or(Decimal::ONE),
|
||||||
|
monthly: base_monthly * Decimal::try_from(1.0 - monthly_discount / 100.0).unwrap_or(Decimal::ONE),
|
||||||
|
yearly: base_yearly * Decimal::try_from(1.0 - yearly_discount / 100.0).unwrap_or(Decimal::ONE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate savings compared to hourly rate
|
||||||
|
pub fn calculate_savings(&self) -> (Decimal, Decimal, Decimal) {
|
||||||
|
let hourly_equivalent_daily = self.hourly * Decimal::from(24);
|
||||||
|
let hourly_equivalent_monthly = self.hourly * Decimal::from(24 * 30);
|
||||||
|
let hourly_equivalent_yearly = self.hourly * Decimal::from(24 * 365);
|
||||||
|
|
||||||
|
let daily_savings = hourly_equivalent_daily - self.daily;
|
||||||
|
let monthly_savings = hourly_equivalent_monthly - self.monthly;
|
||||||
|
let yearly_savings = hourly_equivalent_yearly - self.yearly;
|
||||||
|
|
||||||
|
(daily_savings, monthly_savings, yearly_savings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum SliceType {
|
||||||
|
Basic,
|
||||||
|
Standard,
|
||||||
|
Premium,
|
||||||
|
Custom,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SliceProductBuilder {
|
||||||
|
farmer_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
farmer_name: Option<String>,
|
||||||
|
slice_name: Option<String>,
|
||||||
|
cpu_cores: Option<i32>,
|
||||||
|
memory_gb: Option<i32>,
|
||||||
|
storage_gb: Option<i32>,
|
||||||
|
bandwidth_mbps: Option<i32>,
|
||||||
|
min_uptime_sla: Option<f32>,
|
||||||
|
public_ips: Option<i32>,
|
||||||
|
node_base_data: BaseModelData::new(),
|
||||||
|
// id: Option<String> - moved to base_data,
|
||||||
|
slice_type: Option<crate::models::tfmarketplace::product::SliceType>,
|
||||||
|
price_per_hour: Option<rust_decimal::Decimal>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SliceProductBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn farmer_id(mut self, farmer_id: &str, name: &str) -> Self{
|
||||||
|
self.farmer_id = Some(farmer_id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn farmer_name(mut self, farmer_name: impl Into<String>) -> Self {
|
||||||
|
self.farmer_name = Some(farmer_name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slice_name(mut self, slice_name: impl Into<String>) -> Self {
|
||||||
|
self.slice_name = Some(slice_name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_cores(mut self, cpu_cores: i32) -> Self {
|
||||||
|
self.cpu_cores = Some(cpu_cores);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_gb(mut self, memory_gb: i32) -> Self {
|
||||||
|
self.memory_gb = Some(memory_gb);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn storage_gb(mut self, storage_gb: i32) -> Self {
|
||||||
|
self.storage_gb = Some(storage_gb);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bandwidth_mbps(mut self, bandwidth_mbps: i32) -> Self {
|
||||||
|
self.bandwidth_mbps = Some(bandwidth_mbps);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min_uptime_sla(mut self, min_uptime_sla: f32) -> Self {
|
||||||
|
self.min_uptime_sla = Some(min_uptime_sla);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn public_ips(mut self, public_ips: i32) -> Self {
|
||||||
|
self.public_ips = Some(public_ips);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn node_id(mut self, node_id: &str, name: &str) -> Self{
|
||||||
|
self.node_id = Some(node_id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slice_type(mut self, slice_type: crate::models::tfmarketplace::product::SliceType) -> Self {
|
||||||
|
self.slice_type = Some(slice_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn price_per_hour(mut self, price_per_hour: rust_decimal::Decimal) -> Self {
|
||||||
|
self.price_per_hour = Some(price_per_hour);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<crate::models::tfmarketplace::product::Product, String> {
|
||||||
|
let farmer_id = self.farmer_id.ok_or("farmer_id is required")?;
|
||||||
|
let farmer_name = self.farmer_name.ok_or("farmer_name is required")?;
|
||||||
|
let slice_name = self.slice_name.ok_or("slice_name is required")?;
|
||||||
|
let cpu_cores = self.cpu_cores.ok_or("cpu_cores is required")?;
|
||||||
|
let memory_gb = self.memory_gb.ok_or("memory_gb is required")?;
|
||||||
|
let storage_gb = self.storage_gb.ok_or("storage_gb is required")?;
|
||||||
|
let bandwidth_mbps = self.bandwidth_mbps.ok_or("bandwidth_mbps is required")?;
|
||||||
|
let price_per_hour = self.price_per_hour.ok_or("price_per_hour is required")?;
|
||||||
|
|
||||||
|
let slice_config = crate::models::tfmarketplace::product::SliceConfiguration {
|
||||||
|
cpu_cores,
|
||||||
|
memory_gb,
|
||||||
|
storage_gb,
|
||||||
|
bandwidth_mbps,
|
||||||
|
min_uptime_sla: self.min_uptime_sla.unwrap_or(99.0),
|
||||||
|
public_ips: self.public_ips.unwrap_or(0),
|
||||||
|
node_base_data: BaseModelData::new(),
|
||||||
|
// id: self.node_id - moved to base_data,
|
||||||
|
slice_type: self.slice_type.unwrap_or(crate::models::tfmarketplace::product::SliceType::Basic),
|
||||||
|
pricing: crate::models::tfmarketplace::product::SlicePricing::from_hourly(
|
||||||
|
price_per_hour,
|
||||||
|
5.0, // 5% daily discount
|
||||||
|
15.0, // 15% monthly discount
|
||||||
|
25.0 // 25% yearly discount
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(crate::models::tfmarketplace::product::Product::create_slice_product(
|
||||||
|
farmer_id,
|
||||||
|
farmer_name,
|
||||||
|
slice_name,
|
||||||
|
slice_config,
|
||||||
|
price_per_hour,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
3509
heromodels/src/models/tfmarketplace/user.rs
Normal file
3509
heromodels/src/models/tfmarketplace/user.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user