diff --git a/heromodels/Cargo.lock b/heromodels/Cargo.lock index 2bf5b50..8ddb9d8 100644 --- a/heromodels/Cargo.lock +++ b/heromodels/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.8.12" @@ -37,12 +52,44 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "2.0.1" @@ -69,12 +116,33 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + [[package]] name = "cc" version = "1.2.23" @@ -131,6 +199,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -146,6 +223,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "derive" version = "0.1.0" @@ -154,6 +241,105 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "ethnum" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fast-float2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -177,6 +363,18 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + [[package]] name = "heck" version = "0.5.0" @@ -192,7 +390,11 @@ dependencies = [ "derive", "heromodels-derive", "heromodels_core", + "jsonb", "ourdb", + "postgres", + "r2d2", + "r2d2_postgres", "rhai", "rhai_client_macros", "serde", @@ -220,6 +422,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "iana-time-zone" version = "0.1.63" @@ -244,6 +455,16 @@ dependencies = [ "cc", ] +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.13" @@ -253,12 +474,64 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +dependencies = [ + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", + "windows-sys 0.59.0", +] + +[[package]] +name = "jiff-static" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -269,24 +542,84 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonb" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "153dfebb6ad4484c84a5ae765a18976bf476b8a3f5165ce987aedd59d1f1e6c8" +dependencies = [ + "byteorder", + "ethnum", + "fast-float2", + "itoa", + "jiff", + "nom", + "num-traits", + "ordered-float", + "rand 0.9.2", + "ryu", + "serde", + "serde_json", +] + [[package]] name = "libc" version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -296,6 +629,15 @@ dependencies = [ "spin", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -305,6 +647,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -314,22 +665,144 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "ordered-float" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01" +dependencies = [ + "num-traits", +] + [[package]] name = "ourdb" version = "0.1.0" dependencies = [ "crc32fast", "log", - "rand", + "rand 0.8.5", "thiserror", ] +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "portable-atomic" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "postgres" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "363e6dfbdd780d3aa3597b6eb430db76bb315fa9bad7fae595bb8def808b8470" +dependencies = [ + "bytes", + "fallible-iterator", + "futures-util", + "log", + "tokio", + "tokio-postgres", +] + +[[package]] +name = "postgres-protocol" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" +dependencies = [ + "base64", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand 0.9.2", + "sha2", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", + "serde", + "serde_json", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -363,6 +836,27 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[package]] +name = "r2d2" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "r2d2_postgres" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd4b47636dbca581cd057e2f27a5d39be741ea4f85fd3c29e415c55f71c7595" +dependencies = [ + "postgres", + "r2d2", +] + [[package]] name = "rand" version = "0.8.5" @@ -370,8 +864,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -381,7 +885,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -393,6 +907,24 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + [[package]] name = "rhai" version = "1.21.0" @@ -443,6 +975,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + [[package]] name = "rustversion" version = "1.0.20" @@ -455,6 +993,21 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.219" @@ -481,18 +1034,42 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ + "indexmap", "itoa", "memchr", "ryu", "serde", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + [[package]] name = "smallvec" version = "1.15.0" @@ -510,6 +1087,26 @@ dependencies = [ "version_check", ] +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -522,6 +1119,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strum" version = "0.26.3" @@ -541,6 +1149,12 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -598,6 +1212,77 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", + "socket2 0.6.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-postgres" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand 0.9.2", + "socket2 0.5.10", + "tokio", + "tokio-util", + "whoami", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tst" version = "0.1.0" @@ -606,12 +1291,39 @@ dependencies = [ "thiserror", ] +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unty" version = "0.0.4" @@ -656,6 +1368,12 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -714,6 +1432,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + [[package]] name = "windows-core" version = "0.61.2" @@ -773,6 +1512,88 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/heromodels/Cargo.toml b/heromodels/Cargo.toml index e69ae48..606906d 100644 --- a/heromodels/Cargo.toml +++ b/heromodels/Cargo.toml @@ -15,11 +15,20 @@ tst = { path = "../tst" } heromodels-derive = { path = "../heromodels-derive" } heromodels_core = { path = "../heromodels_core" } rhailib_derive = { package = "derive", path = "../../rhailib/src/derive" } -rhai = { version = "1.21.0", features = ["std", "sync", "decimal", "internals"] } # Added "decimal" feature, sync for Arc> +rhai = { version = "1.21.0", features = [ + "std", + "sync", + "decimal", + "internals", +] } # Added "decimal" feature, sync for Arc> rhai_client_macros = { path = "../rhai_client_macros" } strum = "0.26" strum_macros = "0.26" uuid = { version = "1.17.0", features = ["v4"] } +postgres = { version = "0.19.10", features = ["with-serde_json-1"] } +jsonb = "0.5.2" +r2d2 = "0.8.10" +r2d2_postgres = "0.18.2" [features] default = [] @@ -46,4 +55,4 @@ path = "examples/flow_example.rs" [[example]] name = "biz_rhai" path = "examples/biz_rhai/example.rs" -required-features = ["rhai"] \ No newline at end of file +required-features = ["rhai"] diff --git a/heromodels/examples/postgres_model_example.rs b/heromodels/examples/postgres_model_example.rs new file mode 100644 index 0000000..6f709ff --- /dev/null +++ b/heromodels/examples/postgres_model_example.rs @@ -0,0 +1,225 @@ +use heromodels::db::postgres::Config; +use heromodels::db::{Collection, Db}; +use heromodels::models::userexample::user::user_index::{is_active, username}; +use heromodels::models::{Comment, User}; +use heromodels_core::Model; + +// Helper function to print user details +fn print_user_details(user: &User) { + println!("\n--- User Details ---"); + println!("ID: {}", user.get_id()); + println!("Username: {}", user.username); + println!("Email: {}", user.email); + println!("Full Name: {}", user.full_name); + println!("Active: {}", user.is_active); + println!("Created At: {}", user.base_data.created_at); + println!("Modified At: {}", user.base_data.modified_at); + println!("Comments: {:?}", user.base_data.comments); +} + +// Helper function to print comment details +fn print_comment_details(comment: &Comment) { + println!("\n--- Comment Details ---"); + println!("ID: {}", comment.get_id()); + println!("User ID: {}", comment.user_id); + println!("Content: {}", comment.content); + println!("Created At: {}", comment.base_data.created_at); + println!("Modified At: {}", comment.base_data.modified_at); +} + +fn main() { + let db = heromodels::db::postgres::Postgres::new( + Config::new() + .user(Some("postgres".into())) + .password(Some("test123".into())) + .host(Some("localhost".into())) + .port(Some(5432)), + ) + .expect("Can connect to postgress"); + + println!("Hero Models - Basic Usage Example"); + println!("================================"); + + // Create users with auto-generated IDs + + // User 1 + let user1 = User::new() + .username("johndoe") + .email("john.doe@example.com") + .full_name("John Doe") + .is_active(false) + .build(); + + // User 2 + let user2 = User::new() + .username("janesmith") + .email("jane.smith@example.com") + .full_name("Jane Smith") + .is_active(true) + .build(); + + // User 3 + let user3 = User::new() + .username("willism") + .email("willis.masters@example.com") + .full_name("Willis Masters") + .is_active(true) + .build(); + + // User 4 + let user4 = User::new() + .username("carrols") + .email("carrol.smith@example.com") + .full_name("Carrol Smith") + .is_active(false) + .build(); + + // Save all users to database and get their assigned IDs and updated models + let (user1_id, db_user1) = db + .collection() + .expect("can open user collection") + .set(&user1) + .expect("can set user"); + let (user2_id, db_user2) = db + .collection() + .expect("can open user collection") + .set(&user2) + .expect("can set user"); + let (user3_id, db_user3) = db + .collection() + .expect("can open user collection") + .set(&user3) + .expect("can set user"); + let (user4_id, db_user4) = db + .collection() + .expect("can open user collection") + .set(&user4) + .expect("can set user"); + + println!("User 1 assigned ID: {}", user1_id); + println!("User 2 assigned ID: {}", user2_id); + println!("User 3 assigned ID: {}", user3_id); + println!("User 4 assigned ID: {}", user4_id); + + // We already have the updated models from the set method, so we don't need to retrieve them again + + // Print all users retrieved from database + println!("\n--- Users Retrieved from Database ---"); + println!("\n1. First user:"); + print_user_details(&db_user1); + + println!("\n2. Second user:"); + print_user_details(&db_user2); + + println!("\n3. Third user:"); + print_user_details(&db_user3); + + println!("\n4. Fourth user:"); + print_user_details(&db_user4); + + // Demonstrate different ways to retrieve users from the database + + // 1. Retrieve by username index + println!("\n--- Retrieving Users by Different Methods ---"); + println!("\n1. By Username Index:"); + let stored_users = db + .collection::() + .expect("can open user collection") + .get::("johndoe") + .expect("can load stored user"); + + assert_eq!(stored_users.len(), 1); + print_user_details(&stored_users[0]); + + // 2. Retrieve by active status + println!("\n2. By Active Status (Active = true):"); + let active_users = db + .collection::() + .expect("can open user collection") + .get::(&true) + .expect("can load stored users"); + + assert_eq!(active_users.len(), 2); + for (_i, active_user) in active_users.iter().enumerate() { + print_user_details(active_user); + } + + // 3. Delete a user and show the updated results + println!("\n3. After Deleting a User:"); + let user_to_delete_id = active_users[0].get_id(); + println!("Deleting user with ID: {}", user_to_delete_id); + db.collection::() + .expect("can open user collection") + .delete_by_id(user_to_delete_id) + .expect("can delete existing user"); + + // Show remaining active users + let active_users = db + .collection::() + .expect("can open user collection") + .get::(&true) + .expect("can load stored users"); + + println!(" a. Remaining Active Users:"); + assert_eq!(active_users.len(), 1); + for (_i, active_user) in active_users.iter().enumerate() { + print_user_details(active_user); + } + + // Show inactive users + let inactive_users = db + .collection::() + .expect("can open user collection") + .get::(&false) + .expect("can load stored users"); + + println!(" b. Inactive Users:"); + assert_eq!(inactive_users.len(), 2); + for (_i, inactive_user) in inactive_users.iter().enumerate() { + print_user_details(inactive_user); + } + + println!("\n--- User Model Information ---"); + println!("User DB Prefix: {}", User::db_prefix()); + + // Demonstrate comment creation and association with a user + println!("\n--- Working with Comments ---"); + + // 1. Create and save a comment + println!("\n1. Creating a Comment:"); + let comment = Comment::new() + .user_id(db_user1.get_id()) // commenter's user ID + .content("This is a comment on the user") + .build(); + + // Save the comment and get its assigned ID and updated model + let (comment_id, db_comment) = db + .collection() + .expect("can open comment collection") + .set(&comment) + .expect("can set comment"); + + println!("Comment assigned ID: {}", comment_id); + + println!(" a. Comment Retrieved from Database:"); + print_comment_details(&db_comment); + + // 3. Associate the comment with a user + println!("\n2. Associating Comment with User:"); + let mut updated_user = db_user1.clone(); + updated_user.base_data.add_comment(db_comment.get_id()); + + // Save the updated user and get the new version + let (_, user_with_comment) = db + .collection::() + .expect("can open user collection") + .set(&updated_user) + .expect("can set updated user"); + + println!(" a. User with Associated Comment:"); + print_user_details(&user_with_comment); + + println!("\n--- Model Information ---"); + println!("User DB Prefix: {}", User::db_prefix()); + println!("Comment DB Prefix: {}", Comment::db_prefix()); +} diff --git a/heromodels/src/db.rs b/heromodels/src/db.rs index 5691e11..1576c2f 100644 --- a/heromodels/src/db.rs +++ b/heromodels/src/db.rs @@ -4,6 +4,7 @@ use heromodels_core::{Index, Model}; use serde::{Deserialize, Serialize}; pub mod hero; +pub mod postgres; pub trait Db { /// Error type returned by database operations. diff --git a/heromodels/src/db/fjall.rs b/heromodels/src/db/fjall.rs deleted file mode 100644 index e69de29..0000000 diff --git a/heromodels/src/db/postgres.rs b/heromodels/src/db/postgres.rs new file mode 100644 index 0000000..3b568b5 --- /dev/null +++ b/heromodels/src/db/postgres.rs @@ -0,0 +1,400 @@ +use std::time::Duration; + +use heromodels_core::Model; +use postgres::{Client, NoTls}; +use r2d2::Pool; +use r2d2_postgres::PostgresConnectionManager; + +#[derive(Clone)] +pub struct Postgres { + pool: Pool>, +} + +/// Configuration used to connect to a postgres server. All values are optional, if they are +/// [Option::None], the postgres defaults are used. +#[derive(Default)] +pub struct Config { + pub user: Option, + pub password: Option, + pub dbname: Option, + pub host: Option, + pub port: Option, +} + +impl Config { + /// Create a new empty config + pub fn new() -> Self { + Self::default() + } + + /// Set the user value in the config + pub fn user(mut self, user: Option) -> Self { + self.user = user; + self + } + + /// Set the password value in the config + pub fn password(mut self, password: Option) -> Self { + self.password = password; + self + } + + /// Set the dbname value in the config + pub fn dbname(mut self, dbname: Option) -> Self { + self.dbname = dbname; + self + } + + /// Set the host value in the config + pub fn host(mut self, host: Option) -> Self { + self.host = host; + self + } + + /// Set the port value in the config + pub fn port(mut self, port: Option) -> Self { + self.port = port; + self + } +} + +#[derive(Debug)] +pub enum Error { + /// An error communicating with the postgres server + Postgres(postgres::error::Error), + /// An error originating from the connection pool + ConnectionPool(r2d2::Error), + /// An error encoding to or decoding from json + Json(serde_json::Error), + /// An error encoding to or decoding from jsonb + JsonB(jsonb::Error), + /// We tried to insert a value but the row was not returned + FailedInsert, + /// We tried to update a value but we didn't get the expected 1 modified row + FailedUpdate(usize), + /// We tried to query the existence of a table but it failed + TableExistenceQuery, +} + +impl Postgres { + /// Create a new connection to a postgres instance. + pub fn new(config: Config) -> Result { + let mut cfg = Client::configure(); + cfg.user(config.user.unwrap_or_default().as_ref()) + .password(config.password.unwrap_or_default()) + .dbname(config.dbname.unwrap_or_default().as_ref()) + .host(config.host.unwrap_or_default().as_ref()) + .port(config.port.unwrap_or_default()); + + // No TLS support for now + // let client = cfg.connect(postgres::tls::NoTls)?; + let manager = PostgresConnectionManager::new(cfg, NoTls); + + let pool = Pool::builder() + .connection_timeout(Duration::from_secs(1)) + .max_size(5) + .build(manager)?; + + Ok(Postgres { pool }) + } + + /// Helper method which generates a table name for a model, to alleviate some keyword + /// conflicts. + fn collection_name() -> String { + format!("model_{}", M::db_prefix()) + } +} + +impl super::Db for Postgres { + type Error = Error; + + fn collection( + &self, + ) -> Result, super::Error> { + // Check if the table exists, if not create it with proper indexes + let mut con = self.pool.get().map_err(Error::from)?; + + let row = con + .query_one( + r#"SELECT EXISTS ( +SELECT 1 +FROM information_schema.tables +WHERE table_name = $1 +) AS table_existence;"#, + &[&Self::collection_name::()], + ) + .map_err(Error::from)?; + + let exists: bool = row.get("table_existence"); + + // If table does not exist set it up + if !exists { + eprintln!( + "Table {} does not exist, create it now", + Self::collection_name::() + ); + // Use a transaction here so if index creation failed the table is also not created. + let mut tx = con.transaction().map_err(Error::from)?; + + tx.execute( + &format!( + "CREATE TABLE {} (key SERIAL PRIMARY KEY, value JSONB NOT NULL);", + Self::collection_name::(), + ), + &[], + ) + .map_err(Error::from)?; + + // TODO: Create indexes + + tx.commit().map_err(Error::from)?; + } + + Ok(self.clone()) + } +} + +impl super::Collection<&str, M> for Postgres +where + M: Model, +{ + type Error = Error; + + fn get(&self, key: &Q) -> Result, super::Error> + where + I: heromodels_core::Index, + I::Key: std::borrow::Borrow, + Q: ToString + ?Sized, + { + let mut con = self.pool.get().map_err(Error::from)?; + + Ok(con + .query( + &format!( + "SELECT (value) FROM {} WHERE value->$1 = $2;", + Self::collection_name::(), + ), + &[&I::key(), &key.to_string()], + ) + .map_err(Error::from)? + .into_iter() + .map(|row| serde_json::from_str::(row.get("value")).map_err(Error::from)) + .collect::, _>>()?) + } + + fn get_by_id(&self, id: u32) -> Result, super::Error> { + let mut con = self.pool.get().map_err(Error::from)?; + + if let Some(row) = con + .query( + &format!( + "SELECT (value) FROM {} WHERE key = $1;", + Self::collection_name::() + ), + &[&id], + ) + .map_err(Error::from)? + .into_iter() + .next() + { + Ok(Some( + serde_json::from_str::(row.get("value")).map_err(Error::from)?, + )) + } else { + Ok(None) + } + } + + fn set(&self, value: &M) -> Result<(u32, M), super::Error> { + let mut con = self.pool.get().map_err(Error::from)?; + + // let ser_val = serde_json::to_string(&value).map_err(Error::from)?; + let ser_val = jsonb::to_owned_jsonb(&value).map_err(Error::from)?; + + if value.get_id() == 0 { + // NOTE: We perform a query here since we want the returned value which has the updated ID + let Some(row) = con + .query( + &format!( + "INSERT INTO {} (value) VALUES ($1) RETURNING key, value;", + Self::collection_name::() + ), + &[&postgres::types::Json(value)], + ) + .map_err(Error::from)? + .into_iter() + .next() + else { + return Err(Error::FailedInsert.into()); + }; + + eprintln!("insert done"); + + // Get the generated ID + let id = row.get::<_, i32>("key") as u32; + let mut value = row.get::<_, postgres::types::Json>("value").0; + // .map_err(Error::from)?; + value.base_data_mut().id = id; + + // NOTE: Update the value so the id is set correctly in the value itself + // let ser_val = serde_json::to_string(&value).map_err(Error::from)?; + + let updated = con + .execute( + &format!( + "UPDATE {} SET value = $1 WHERE key = $2;", + Self::collection_name::() + ), + &[ + &postgres::types::Json(value.clone()), + &(value.get_id() as i32), + ], + ) + .map_err(Error::from)?; + + if updated != 1 { + return Err(Error::FailedUpdate(updated as usize).into()); + } + + Ok((id, value)) + } else { + let updated = con + .execute( + &format!( + "UPDATE {} SET value = $1 WHERE key = $2;", + Self::collection_name::(), + ), + &[ + &postgres::types::Json(value.clone()), + &(value.get_id() as i32), + ], + ) + .map_err(Error::from)?; + + if updated != 1 { + return Err(Error::FailedUpdate(updated as usize).into()); + } + + Ok((value.get_id(), value.clone())) + } + } + + fn delete(&self, key: &Q) -> Result<(), super::Error> + where + I: heromodels_core::Index, + I::Key: std::borrow::Borrow, + Q: ToString + ?Sized, + { + let mut con = self.pool.get().map_err(Error::from)?; + + con.execute( + &format!( + "DELETE FROM {} WHERE value->$1 = $2;", + Self::collection_name::() + ), + &[&I::key(), &key.to_string()], + ) + .map_err(Error::from)?; + + Ok(()) + } + + fn delete_by_id(&self, id: u32) -> Result<(), super::Error> { + let mut con = self.pool.get().map_err(Error::from)?; + + con.execute( + &format!( + "DELETE FROM {} WHERE key = $1;", + Self::collection_name::() + ), + &[&id], + ) + .map_err(Error::from)?; + + Ok(()) + } + + fn get_all(&self) -> Result, super::Error> { + let mut con = self.pool.get().map_err(Error::from)?; + + Ok(con + .query( + &format!("SELECT (value) FROM {};", Self::collection_name::()), + &[], + ) + .map_err(Error::from)? + .into_iter() + .map(|row| serde_json::from_str::(row.get("value")).map_err(Error::from)) + .collect::, _>>()?) + } + + fn begin_transaction( + &self, + ) -> Result>, super::Error> { + todo!() + } +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Postgres(e) => f.write_fmt(format_args!("postgres error: {e}")), + Self::ConnectionPool(e) => { + f.write_fmt(format_args!("postgres connection pool error: {e}")) + } + Self::Json(e) => { + f.write_fmt(format_args!("could not decode from or encode to json: {e}")) + } + Self::JsonB(e) => f.write_fmt(format_args!( + "could not decode from or encode to jsonb: {e}" + )), + Self::FailedInsert => f.write_str("insert did not return any result"), + Self::FailedUpdate(amount) => f.write_fmt(format_args!( + "update did not return the expected 1 modified row (got {amount})" + )), + Self::TableExistenceQuery => f.write_str("query to check if table exists failed"), + } + } +} + +impl core::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Postgres(e) => Some(e), + Self::ConnectionPool(e) => Some(e), + Self::Json(e) => Some(e), + Self::JsonB(e) => Some(e), + _ => None, + } + } +} + +impl From for Error { + fn from(value: postgres::error::Error) -> Self { + Self::Postgres(value) + } +} + +impl From for Error { + fn from(value: r2d2::Error) -> Self { + Self::ConnectionPool(value) + } +} + +impl From for Error { + fn from(value: serde_json::Error) -> Self { + Self::Json(value) + } +} + +impl From for Error { + fn from(value: jsonb::Error) -> Self { + Self::JsonB(value) + } +} + +impl From for super::Error { + fn from(value: Error) -> Self { + super::Error::DB(value) + } +}