From 1faa3b96682d5071f8586724506da86752c1c4c2 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Mon, 7 Oct 2024 20:53:58 +0200 Subject: [PATCH] postgres --- Cargo.lock | 1149 ++++++++++++++++++----------------- Cargo.toml | 7 +- docker-compose.yml | 21 +- migrations/0000_init.sql | 32 + src/db.rs | 7 +- src/flow.rs | 245 +++----- src/item.rs | 52 +- src/json_store.rs | 1 + src/location.rs | 91 ++- src/main.rs | 64 +- src/routes/flow.rs | 67 +- src/routes/item/demand.rs | 21 +- src/routes/item/location.rs | 10 +- src/routes/item/mod.rs | 37 +- src/routes/item/stat.rs | 2 +- src/routes/item/supply.rs | 16 +- src/routes/mod.rs | 17 + src/transaction.rs | 210 +++---- src/variant.rs | 253 +++----- 19 files changed, 1058 insertions(+), 1244 deletions(-) create mode 100644 migrations/0000_init.sql diff --git a/Cargo.lock b/Cargo.lock index 8cec807..2147c51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", "once_cell", "version_check", "zerocopy", @@ -39,6 +38,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -122,7 +127,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -133,7 +138,16 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", ] [[package]] @@ -172,18 +186,24 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "binascii" version = "0.1.4" @@ -201,17 +221,8 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty", - "radium", - "tap", - "wyz", + "serde", ] [[package]] @@ -223,27 +234,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bson" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068208f2b6fcfa27a7f1ee37488d2bb8ba2640f68f5475d08e1d9130696aba59" -dependencies = [ - "ahash", - "base64 0.13.1", - "bitvec", - "hex", - "indexmap", - "js-sys", - "once_cell", - "rand", - "serde", - "serde_bytes", - "serde_json", - "time", - "uuid", -] - [[package]] name = "bumpalo" version = "3.16.0" @@ -268,12 +258,6 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" -[[package]] -name = "case" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" - [[package]] name = "cc" version = "1.1.21" @@ -292,14 +276,13 @@ dependencies = [ "futures", "log", "mdq", - "mongod", - "mongodb", "reqwest", "rocket", "rocket_cors", "serde", "serde_json", "serde_yaml", + "sqlx", "tokio", "toml", "uuid", @@ -322,6 +305,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.6", ] @@ -344,7 +328,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] @@ -372,10 +356,19 @@ dependencies = [ ] [[package]] -name = "convert_case" -version = "0.4.0" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cookie" @@ -413,6 +406,36 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crossterm" version = "0.27.0" @@ -467,46 +490,16 @@ dependencies = [ ] [[package]] -name = "darling" -version = "0.13.4" +name = "der" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "darling_core", - "darling_macro", + "const-oid", + "pem-rfc7468", + "zeroize", ] -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - [[package]] name = "deranged" version = "0.3.11" @@ -516,30 +509,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version 0.4.1", - "syn 2.0.77", -] - [[package]] name = "devise" version = "0.4.2" @@ -570,7 +539,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -580,15 +549,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] [[package]] name = "encoding_rs" @@ -599,18 +578,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "enum-as-inner" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "env_filter" version = "0.1.2" @@ -650,6 +617,28 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.1.1" @@ -670,6 +659,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -700,12 +700,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.30" @@ -748,6 +742,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.30" @@ -762,7 +767,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -865,12 +870,19 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] -name = "heck" -version = "0.4.1" +name = "hashlink" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", +] [[package]] name = "heck" @@ -896,6 +908,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -906,14 +927,12 @@ dependencies = [ ] [[package]] -name = "hostname" -version = "0.3.1" +name = "home" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "libc", - "match_cfg", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -984,7 +1003,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", "tower-service", "tracing", @@ -1027,23 +1046,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -1071,18 +1073,6 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2 0.5.7", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - [[package]] name = "ipnet" version = "2.10.0" @@ -1137,6 +1127,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1145,10 +1138,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "libm" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "linux-raw-sys" @@ -1187,21 +1191,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -1211,12 +1200,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "md-5" version = "0.10.6" @@ -1258,6 +1241,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1279,80 +1268,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "mongod" -version = "0.2.2" -source = "git+https://git.hydrar.de/jmarya/mongod#58150002e60802d7b00fe0537e4a2e8c4e389a4a" -dependencies = [ - "chrono", - "futures", - "mongod_derive", - "mongodb", - "regex", - "serde", - "serde_json", - "tokio", - "uuid", -] - -[[package]] -name = "mongod_derive" -version = "0.1.0" -source = "git+https://git.hydrar.de/jmarya/mongod#58150002e60802d7b00fe0537e4a2e8c4e389a4a" -dependencies = [ - "case", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "mongodb" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef206acb1b72389b49bc9985efe7eb1f8a9bb18e5680d262fac26c07f44025f1" -dependencies = [ - "async-trait", - "base64 0.13.1", - "bitflags 1.3.2", - "bson", - "chrono", - "derivative", - "derive_more", - "futures-core", - "futures-executor", - "futures-io", - "futures-util", - "hex", - "hmac", - "lazy_static", - "md-5", - "pbkdf2", - "percent-encoding", - "rand", - "rustc_version_runtime", - "rustls", - "rustls-pemfile", - "serde", - "serde_bytes", - "serde_with", - "sha-1", - "sha2", - "socket2 0.4.10", - "stringprep", - "strsim 0.10.0", - "take_mut", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-util", - "trust-dns-proto", - "trust-dns-resolver", - "typed-builder", - "uuid", - "webpki-roots", -] - [[package]] name = "multer" version = "3.1.0" @@ -1389,6 +1304,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1399,12 +1324,49 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1412,6 +1374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1462,7 +1425,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -1489,6 +1452,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1513,13 +1482,10 @@ dependencies = [ ] [[package]] -name = "pbkdf2" -version = "0.11.0" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", -] +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pear" @@ -1541,7 +1507,16 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.77", + "syn", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", ] [[package]] @@ -1562,6 +1537,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -1600,17 +1596,11 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", "version_check", "yansi", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.37" @@ -1620,12 +1610,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.8.5" @@ -1682,7 +1666,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -1769,31 +1753,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - [[package]] name = "rocket" version = "0.5.1" @@ -1844,7 +1803,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.77", + "syn", "unicode-xid", "version_check", ] @@ -1893,40 +1852,32 @@ dependencies = [ "uncased", ] +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver 1.0.23", -] - -[[package]] -name = "rustc_version_runtime" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d31b7153270ebf48bf91c65ae5b0c00e749c4cfad505f66530ac74950249582f" -dependencies = [ - "rustc_version 0.2.3", - "semver 0.9.0", -] - [[package]] name = "rustix" version = "0.38.37" @@ -1940,18 +1891,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -1961,16 +1900,6 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.17" @@ -2013,16 +1942,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "security-framework" version = "2.11.1" @@ -2046,27 +1965,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.210" @@ -2076,15 +1974,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_bytes" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" version = "1.0.210" @@ -2093,7 +1982,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2102,7 +1991,6 @@ version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "indexmap", "itoa", "memchr", "ryu", @@ -2130,28 +2018,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -2166,10 +2032,10 @@ dependencies = [ ] [[package]] -name = "sha-1" -version = "0.10.1" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -2211,6 +2077,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "slab" version = "0.4.9" @@ -2225,15 +2101,8 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ - "libc", - "winapi", + "serde", ] [[package]] @@ -2251,6 +2120,232 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" +dependencies = [ + "atoi", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "native-tls", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url", + "uuid", +] [[package]] name = "stable-pattern" @@ -2281,12 +2376,6 @@ dependencies = [ "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -2305,11 +2394,11 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn", ] [[package]] @@ -2318,17 +2407,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.77" @@ -2367,18 +2445,6 @@ dependencies = [ "libc", ] -[[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tempfile" version = "3.12.0" @@ -2409,7 +2475,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2481,7 +2547,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2", "tokio-macros", "windows-sys 0.52.0", ] @@ -2494,7 +2560,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2507,16 +2573,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.16" @@ -2536,7 +2592,6 @@ checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", - "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -2588,6 +2643,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2601,7 +2657,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2643,68 +2699,12 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trust-dns-proto" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.2.3", - "ipnet", - "lazy_static", - "log", - "rand", - "smallvec", - "thiserror", - "tinyvec", - "tokio", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "parking_lot", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "trust-dns-proto", -] - [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typed-builder" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "typenum" version = "1.17.0" @@ -2772,9 +2772,9 @@ dependencies = [ [[package]] name = "unicode-properties" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-width" @@ -2788,18 +2788,18 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "unsafe-libyaml" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "url" version = "2.5.2" @@ -2807,7 +2807,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -2870,6 +2870,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[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.93" @@ -2892,7 +2898,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn", "wasm-bindgen-shared", ] @@ -2926,7 +2932,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2948,16 +2954,14 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "0.25.4" +name = "whoami" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "widestring" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", +] [[package]] name = "winapi" @@ -3175,15 +3179,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "yansi" version = "1.0.1" @@ -3211,5 +3206,11 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 99c3aeb..61d9dd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -chrono = "0.4.38" futures = "0.3.30" log = "0.4.20" mdq = { git = "https://git.hydrar.de/mdtools/mdq" } -mongodb = "2.8.0" rocket = { version = "0.5.1", features = ["json"] } rocket_cors = "0.6.0" serde = { version = "1.0.195", features = ["derive"] } @@ -16,8 +14,9 @@ serde_json = "1.0.111" serde_yaml = "0.9.34" tokio = { version = "1.35.1", features = ["full"] } toml = "0.8.8" -uuid = { version = "1.8.0", features = ["v4"] } -mongod = { git = "https://git.hydrar.de/jmarya/mongod" } env_logger = "0.11.5" walkdir = "2.5.0" reqwest = { version = "0.11", features = ["json"] } +chrono = { version = "0.4.38", features = ["serde"] } +uuid = { version = "1.8.0", features = ["v4", "serde"] } +sqlx = { version = "0.8", features = ["postgres", "runtime-tokio-native-tls", "derive", "uuid", "chrono", "json"] } diff --git a/docker-compose.yml b/docker-compose.yml index de13c60..633cce5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,25 +5,26 @@ services: ports: - "8080:8080" depends_on: - - mongodb + - postgres volumes: - ./itemdb/items:/itemdb - ./locations:/locations - ./flows:/flows - ./config.toml:/config.toml environment: - - "DB_URI=mongodb://user:pass@mongodb:27017" - - "DB=cdb" + - "DATABASE_URL=postgres://user:pass@postgres/cdb" - "RUST_LOG=debug" - "ROCKET_ADDRESS=0.0.0.0" - "ROCKET_PORT=8080" - mongodb: - image: mongo:latest + postgres: + image: timescale/timescaledb:latest-pg16 + restart: always ports: - - "27017:27017" - environment: - MONGO_INITDB_ROOT_USERNAME: user - MONGO_INITDB_ROOT_PASSWORD: pass + - 5432:5432 volumes: - - ./db:/data/db + - ./db:/var/lib/postgresql/data/ + environment: + - POSTGRES_USER=user + - POSTGRES_PASSWORD=pass + - POSTGRES_DB=cdb diff --git a/migrations/0000_init.sql b/migrations/0000_init.sql new file mode 100644 index 0000000..d617aa1 --- /dev/null +++ b/migrations/0000_init.sql @@ -0,0 +1,32 @@ +CREATE TABLE flows ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + "started" timestamptz NOT NULL DEFAULT current_timestamp, + kind TEXT NOT NULL, + input UUID[], + ended timestamptz, + "next" UUID, + produced UUID[], + FOREIGN KEY "next" REFERENCES flows(id) +); + +CREATE TABLE flow_notes ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + time timestamptz NOT NULL DEFAULT current_timestamp, + content TEXT NOT NULL, + on_flow UUID NOT NULL, + FOREIGN KEY on_flow REFERENCES flows(id) +); + +CREATE TABLE transactions ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + created timestamptz NOT NULL DEFAULT current_timestamp, + item TEXT NOT NULL, + variant TEXT NOT NULL, + price NUMERIC(2), + origin TEXT, + "location" TEXT, + note TEXT, + destination TEXT, + consumed_price NUMERIC(2), + consumed_timestamp timestamptz +); diff --git a/src/db.rs b/src/db.rs index 5cadf24..c45aa4d 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,10 +1,9 @@ use std::collections::HashMap; -use mongod::Model; - use crate::item::Item; /// Item database +#[derive(Debug)] pub struct ItemDB { index: HashMap, } @@ -20,9 +19,7 @@ impl ItemDB { for item in &index.documents { let item = Item::new(item); - item.insert_overwrite().await.unwrap(); - log::info!("Adding item {} to DB", item.name); - items.insert(item._id.clone(), item); + items.insert(item.id.clone(), item); } Self { index: items } diff --git a/src/flow.rs b/src/flow.rs index 33983e7..75dc536 100644 --- a/src/flow.rs +++ b/src/flow.rs @@ -1,11 +1,16 @@ +use serde::{Deserialize, Serialize}; +use serde_json::json; use std::collections::HashMap; -use mongod::{assert_reference_of, reference_of, Reference, Validate}; +use crate::routes::item::{item_does_not_exist_error, variant_does_not_exist_error, SupplyForm}; +use crate::routes::{ApiError, ToAPI}; +use crate::{get_itemdb, get_pg}; +use sqlx::FromRow; -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct FlowInfo { #[serde(default)] - pub _id: String, + pub id: String, pub name: String, #[serde(default)] pub depends: Vec, @@ -13,23 +18,10 @@ pub struct FlowInfo { pub produces: Option>, } -impl Validate for FlowInfo { - async fn validate(&self) -> Result<(), String> { - Ok(()) - } -} - -impl FlowInfo { - pub async fn add(&mut self, id: &str) { - self._id = id.to_string(); - self.insert_overwrite().await.unwrap(); - } -} - impl ToAPI for FlowInfo { async fn api(&self) -> serde_json::Value { json!({ - "id": self._id, + "id": self.id, "name": self.name, "depends": self.depends, "next": self.next, @@ -37,119 +29,68 @@ impl ToAPI for FlowInfo { }) } } - -use mongod::{ - derive::{Model, Referencable}, - Model, Referencable, ToAPI, -}; -use mongodb::bson::doc; -use serde::{Deserialize, Serialize}; -use serde_json::json; - -use crate::item::Item; -use crate::routes::item::{item_does_not_exist_error, variant_does_not_exist_error, SupplyForm}; -use crate::routes::{api_error, ApiError}; -use crate::transaction::{Price, Transaction}; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct DoneInfo { - /// Timestamp when the flow was ended - pub ended: i64, - /// The flow succedding this one - pub next: Option, - /// Transactions this flow produced - pub produced: Option>, -} - -impl DoneInfo { - pub fn new(next: Option) -> Self { - Self { - ended: chrono::Utc::now().timestamp(), - next, - produced: None, - } - } - - pub fn api(&self) -> serde_json::Value { - json!({ - "ended": self.ended, - "next": self.next.as_ref().map(|x| x.id()), - "produced": self.produced.as_ref().map(|x| x.iter().map(|t| t.id()).collect::>()), - }) - } -} - /// A production flow -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Flow { /// ID - pub _id: String, + pub id: uuid::Uuid, /// Tiemstamp when the flow was started - pub started: i64, + pub started: chrono::DateTime, /// Kind of flow; ID of the describing JSON - pub kind: Reference, + pub kind: String, /// Input transactions - pub input: Option>, - /// Information when a flow is done - pub done: Option, -} - -impl Validate for Flow { - async fn validate(&self) -> Result<(), String> { - assert_reference_of!(self.kind, FlowInfo); - - if let Some(input) = &self.input { - for t in input { - assert_reference_of!(t, Transaction); - } - } - - if let Some(done) = &self.done { - if let Some(next) = &done.next { - assert_reference_of!(next, Flow); - } - if let Some(produced) = &done.produced { - for prod in produced { - assert_reference_of!(prod, Transaction); - } - } - } - - Ok(()) - } + pub input: Option>, + /// Timestamp when the flow was ended + pub ended: Option>, + /// The flow succedding this one + pub next: Option, + /// Transactions this flow produced + pub produced: Option>, } impl ToAPI for Flow { async fn api(&self) -> serde_json::Value { + let done = if self.ended.is_some() { + Some(json!({ + "ended": self.ended.map(|x| x.timestamp()), + "next": self.next, + "produced": self.produced + })) + } else { + None + }; + json!({ - "id": self._id, - "started": self.started, - "kind": self.kind.id(), - "input": self.input.as_ref().map(|x| x.iter().map(|t| t.id()).collect::>()), - "done": self.done.as_ref().map(|x| x.api()) + "id": self.id, + "started": self.started.timestamp(), + "kind": self.kind, + "input": self.input, + "done": done }) } } impl Flow { - pub async fn create(kind: &str, input: Option>) -> Self { - let f = Self { - _id: uuid::Uuid::new_v4().to_string(), - started: chrono::Utc::now().timestamp(), - kind: reference_of!(FlowInfo, kind).unwrap(), - input: input, - done: None, - }; + pub async fn get(id: &uuid::Uuid) -> Option { + sqlx::query_as("SELECT * FROM flows WHERE id = $1") + .bind(id) + .fetch_optional(get_pg!()) + .await + .unwrap() + } - f.insert().await.unwrap(); - - f + pub async fn create(kind: &str, input: Option>) -> Self { + sqlx::query_as("INSERT INTO flows (kind, input) VALUES ($1, $2) RETURNING *") + .bind(kind) + .bind(input) + .fetch_one(get_pg!()) + .await + .unwrap() } pub async fn end(self) -> Self { - self.change() - .done(Some(DoneInfo::new(None))) - .update() + sqlx::query_as("UPDATE flows SET ended = current_timestamp RETURNING *") + .fetch_one(get_pg!()) .await .unwrap() } @@ -157,14 +98,14 @@ impl Flow { pub async fn end_with_produce( self, produced: &[SupplyForm], - ) -> Result>, ApiError> { + ) -> Result>, ApiError> { let mut ret = HashMap::new(); let mut t_create = Vec::new(); let mut produced_ref = Vec::with_capacity(ret.len()); for prod in produced { - let t = Item::get(&prod.item) - .await + let t = get_itemdb!() + .get_item(&prod.item) .ok_or_else(item_does_not_exist_error)? .variant(&prod.variant) .ok_or_else(variant_does_not_exist_error)?; @@ -175,83 +116,73 @@ impl Flow { for (item, info) in t_create { let t = item .supply( - Price::zero(), - Some(&format!("flow::{}::{}", self.kind.id(), self._id)), + 0.0, + Some(&format!("flow::{}::{}", self.kind, self.id)), info.location.as_ref().map(|x| x.as_str()), info.note.as_ref().map(|x| x.as_str()), ) .await; ret.entry(item.item_variant_id().clone()) .or_insert(Vec::new()) - .push(t._id.clone()); - produced_ref.push(t.reference()); + .push(t.id.clone()); + produced_ref.push(t.id); } - self.change() - .done(Some(DoneInfo { - ended: chrono::Utc::now().timestamp(), - next: None, - produced: Some(produced_ref), - })) - .update() - .await - .unwrap(); + sqlx::query("UPDATE transactions SET consumed_timestamp = current_timestamp, produced = $1 WHERE id = $2") + .bind(produced_ref) + .bind(self.id) + .execute(get_pg!()).await.unwrap(); Ok(ret) } pub async fn continue_next(self, next_flow: &Flow) -> Self { - self.change() - .done(Some(DoneInfo::new(Some(next_flow.reference())))) - .update() - .await - .unwrap() + sqlx::query_as("UPDATE transactions SET consumed_timestamp = current_timestamp, \"next\" = $1 WHERE id = $2 RETURNING *") + .bind(next_flow.id) + .bind(&self.id) + .fetch_one(get_pg!()).await.unwrap() } } /// A note for a Flow -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct FlowNote { /// ID - pub _id: String, - /// Tiemstamp when the note was created - pub timestamp: i64, + pub id: uuid::Uuid, + /// Timestamp when the note was created + pub time: chrono::DateTime, /// Note Content pub content: String, /// Associated flow - pub on_flow: Reference, + pub on_flow: uuid::Uuid, } impl FlowNote { - pub async fn create(content: &str, flow: Reference) -> Self { - let s = Self { - _id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now().timestamp(), - content: content.to_string(), - on_flow: flow, - }; + pub async fn create(content: &str, flow: &uuid::Uuid) -> Self { + sqlx::query_as("INSERT INTO flow_notes (content, on_flow) VALUES ($1, $2) RETURNING *") + .bind(content) + .bind(flow) + .fetch_one(get_pg!()) + .await + .unwrap() + } - s.insert().await.unwrap(); - - s + pub async fn find_of(id: &str) -> Vec { + sqlx::query_as("SELECT * FROM flow_notes WHERE on_flow = $1 ORDER BY time DESC") + .bind(id) + .fetch_all(get_pg!()) + .await + .unwrap() } } impl ToAPI for FlowNote { async fn api(&self) -> serde_json::Value { json!({ - "uuid": self._id, - "timestamp": self.timestamp, + "uuid": self.id, + "timestamp": self.time.timestamp(), "content": self.content, - "on_flow": self.on_flow.id() + "on_flow": self.on_flow }) } } - -impl Validate for FlowNote { - async fn validate(&self) -> Result<(), String> { - assert_reference_of!(self.on_flow, Flow); - - Ok(()) - } -} diff --git a/src/item.rs b/src/item.rs index de77385..f08f72e 100644 --- a/src/item.rs +++ b/src/item.rs @@ -1,13 +1,9 @@ use std::collections::HashMap; -use mongod::{ - derive::{Model, Referencable}, - Model, Validate, -}; -use mongodb::bson::doc; use serde::{Deserialize, Serialize}; use serde_json::json; +use crate::get_pg; use crate::transaction::Transaction; use crate::variant::Variant; @@ -24,10 +20,10 @@ use crate::variant::Variant; /// This struct serves as a blueprint for describing individual items within /// a larger inventory or database of physical goods. It includes fields for /// the name of the item and its category. -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Item { /// The ID - pub _id: String, + pub id: String, /// The name of the Item pub name: String, /// Category of the Item @@ -37,12 +33,6 @@ pub struct Item { pub variants: HashMap, } -impl Validate for Item { - async fn validate(&self) -> Result<(), String> { - Ok(()) - } -} - impl Item { /// Creates a new `Item` from a parsed markdown document pub fn new(doc: &mdq::Document) -> Self { @@ -93,7 +83,7 @@ impl Item { } Self { - _id: id, + id, name, category, variants, @@ -113,33 +103,23 @@ impl Item { } pub async fn inventory(&self) -> Vec { - let filter = doc! { - "item": &self._id, - "consumed": { "$not": { "$type": "object" } } - }; - - Transaction::find(filter, None, None).await.unwrap() + sqlx::query_as("SELECT * FROM transactions WHERE item = $1 AND consumed_timestamp IS NULL ORDER BY created DESC") + .bind(&self.id) + .fetch_all(get_pg!()).await.unwrap() } pub async fn inventory_by_origin(&self, origin: &str) -> Vec { - let filter = doc! { - "item": &self._id, - "consumed": { "$not": { "$type": "object" } }, - "origin": origin - }; - - Transaction::find(filter, None, None).await.unwrap() + sqlx::query_as("SELECT * FROM transactions WHERE item = $1 AND consumed_timestamp IS NULL AND origin = $2 ORDER BY created DESC") + .bind(&self.id) + .bind(origin) + .fetch_all(get_pg!()).await.unwrap() } pub async fn consumed_by_destination(&self, destination: &str) -> Vec { - let filter = doc! { - "item": &self._id, - "consumed": { - "destination": destination - } - }; - - Transaction::find(filter, None, None).await.unwrap() + sqlx::query_as("SELECT * FROM transactions WHERE item = $1 AND consumed_timestamp IS NOT NULL AND destination = $2 ORDER BY created DESC") + .bind(&self.id) + .bind(destination) + .fetch_all(get_pg!()).await.unwrap() } pub fn api_json(&self) -> serde_json::Value { @@ -150,7 +130,7 @@ impl Item { .collect(); json!({ - "uuid": self._id, + "uuid": self.id, "name": self.name, "category": self.category, "variants": variants diff --git a/src/json_store.rs b/src/json_store.rs index 4cd67ec..ad2fd24 100644 --- a/src/json_store.rs +++ b/src/json_store.rs @@ -5,6 +5,7 @@ use std::{ use serde::Deserialize; +#[derive(Debug)] pub struct JSONStore { documents: HashMap, } diff --git a/src/location.rs b/src/location.rs index dfa99ee..81bd6fe 100644 --- a/src/location.rs +++ b/src/location.rs @@ -1,18 +1,14 @@ -use futures::FutureExt; -use mongod::{ - derive::{Model, Referencable}, - Model, ToAPI, Validate, -}; -use mongodb::bson::doc; use serde::{Deserialize, Serialize}; use serde_json::json; +use crate::{get_locations, routes::ToAPI}; + /// A Storage Location -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Location { /// UUID #[serde(default)] - pub _id: String, + pub id: String, /// Name pub name: String, /// Parent @@ -27,73 +23,64 @@ pub struct StorageConditions { pub temperature: i64, } -impl Validate for Location { - async fn validate(&self) -> Result<(), String> { - Ok(()) - } -} - impl Location { /// Recursively get the conditions of a location. This inherits from parent locations. - pub fn conditions_rec(&self) -> futures::future::BoxFuture<'_, Option> { - async move { - if let Some(cond) = &self.conditions { - return Some(cond.clone()); - } + pub fn conditions_rec(&self) -> Option { + let locations = get_locations!(); - if let Some(parent) = &self.parent { - if let Some(parent_loc) = Location::get(parent).await { - if let Some(cond) = parent_loc.conditions_rec().await { - return Some(cond); - } + if let Some(cond) = &self.conditions { + return Some(cond.clone()); + } + + if let Some(parent) = &self.parent { + if let Some(parent_loc) = locations.get(parent) { + if let Some(cond) = parent_loc.conditions_rec() { + return Some(cond); } } - - None } - .boxed() + + None } // Get direct children - pub async fn children_direct(&self) -> Vec { - Location::find(doc! { "parent": self._id.clone()}, None, None) - .await - .unwrap() + pub fn children_direct(&self) -> Vec { + let mut ret = Vec::new(); + + let locations = get_locations!(); + for loc in locations.keys() { + let loc = locations.get(loc).unwrap(); + if *loc.parent.as_ref().unwrap_or(&String::new()) == self.id { + ret.push(loc.clone()); + } + } + + ret } // Get all children locations - pub fn children_recursive(&self) -> futures::future::BoxFuture<'_, Vec> { - async move { - let mut all = Vec::new(); + pub fn children_recursive(&self) -> Vec { + let mut all = Vec::new(); - let direct = self.children_direct().await; - all.extend_from_slice(&direct); + let direct = self.children_direct(); + all.extend_from_slice(&direct); - for loc in direct { - let sub = loc.children_recursive().await; - all.extend_from_slice(&sub); - } - - all + for loc in direct { + let sub = loc.children_recursive(); + all.extend_from_slice(&sub); } - .boxed() + + all } } impl ToAPI for Location { async fn api(&self) -> serde_json::Value { json!({ - "id": self._id, + "id": self.id, "name": self.name, "parent": self.parent, - "conditions": self.conditions_rec().await + "conditions": self.conditions_rec() }) } } - -impl Location { - pub async fn add(&mut self, id: &str) { - self._id = id.to_string(); - self.insert_overwrite().await.unwrap(); - } -} diff --git a/src/main.rs b/src/main.rs index e003158..c7bd59f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use location::Location; use rocket::routes as route; use rocket::{http::Method, launch}; +use tokio::sync::OnceCell; mod config; mod db; @@ -16,6 +17,55 @@ mod routes; mod transaction; mod variant; +pub static PG: OnceCell = OnceCell::const_new(); + +#[macro_export] +macro_rules! get_pg { + () => { + if let Some(client) = $crate::PG.get() { + client + } else { + let client = sqlx::postgres::PgPoolOptions::new() + .max_connections(5) + .connect(&std::env::var("DATABASE_URL").unwrap()) + .await + .unwrap(); + $crate::PG.set(client).unwrap(); + $crate::PG.get().unwrap() + } + }; +} + +pub static ITEMDB: OnceCell = OnceCell::const_new(); + +#[macro_export] +macro_rules! get_itemdb { + () => { + if let Some(client) = $crate::ITEMDB.get() { + client + } else { + let itemdb = $crate::db::ItemDB::new("./itemdb").await; + $crate::ITEMDB.set(itemdb).unwrap(); + $crate::ITEMDB.get().unwrap() + } + }; +} + +pub static LOCATIONS: OnceCell> = OnceCell::const_new(); + +#[macro_export] +macro_rules! get_locations { + () => { + if let Some(client) = $crate::LOCATIONS.get() { + client + } else { + let locations = $crate::JSONStore::new("./locations"); + $crate::LOCATIONS.set(locations).unwrap(); + $crate::LOCATIONS.get().unwrap() + } + }; +} + // ░░░░░░░░░░▀▀▀██████▄▄▄░░░░░░░░░░ // ░░░░░░░░░░░░░░░░░▀▀▀████▄░░░░░░░ // ░░░░░░░░░░▄███████▀░░░▀███▄░░░░░ @@ -49,19 +99,11 @@ async fn rocket() -> _ { .expect("error creating CORS options"); let config = config::get_config(); - let itemdb = db::ItemDB::new("./itemdb").await; - let mut locations: JSONStore = JSONStore::new("./locations"); - let mut flows: JSONStore = JSONStore::new("./flows"); + let itemdb = get_itemdb!(); + let locations = get_locations!(); + let flows: JSONStore = JSONStore::new("./flows"); integrity::verify_integrity(&config, &flows, &locations, &itemdb).await; - for location in &mut *locations { - location.1.add(location.0).await; - } - - for flow in &mut *flows { - flow.1.add(flow.0).await; - } - rocket::build() .mount( "/", diff --git a/src/routes/flow.rs b/src/routes/flow.rs index af343af..d6da6e4 100644 --- a/src/routes/flow.rs +++ b/src/routes/flow.rs @@ -1,18 +1,16 @@ -use std::collections::HashMap; - -use mongod::{reference_of, vec_to_api, Model, Referencable, Sort, ToAPI}; -use mongodb::bson::doc; use rocket::{get, post, serde::json::Json, State}; use serde::{Deserialize, Serialize}; use serde_json::json; +use std::{collections::HashMap, str::FromStr}; use crate::{ check_auth, config::Config, flow::{Flow, FlowInfo, FlowNote}, + get_pg, json_store::JSONStore, - routes::{api_error, FallibleApiResponse, Token}, - transaction::{Price, Transaction}, + routes::{api_error, vec_to_api, FallibleApiResponse, ToAPI, Token}, + transaction::Transaction, }; use super::{item::SupplyForm, ApiError}; @@ -42,16 +40,11 @@ pub async fn active_flows_route( let flowinfo = flows.get(id).ok_or_else(|| api_error("Flow not found"))?; - let flow = Flow::find( - doc! { - "kind": flowinfo.reference(), - "done": { "$not": { "$type": "object" } } - }, - None, - None, - ) - .await - .unwrap(); + let flow: Vec = sqlx::query_as("SELECT * FROM flows WHERE kind = $1 AND ended IS NULL") + .bind(&flowinfo.id) + .fetch_all(get_pg!()) + .await + .unwrap(); Ok(json!(vec_to_api(&flow).await)) } @@ -85,9 +78,10 @@ pub async fn create_flow( // verify valid input transactions if let Some(input) = &form.input { for t in input { - let t = Transaction::get(t) - .await - .ok_or_else(|| api_error(&format!("No such transaction {t}")))?; + let t = + Transaction::get(&uuid::Uuid::from_str(t).map_err(|_| api_error("Invalid UUID"))?) + .await + .ok_or_else(|| api_error(&format!("No such transaction {t}")))?; let item_variant = format!("{}::{}", t.item, t.variant); @@ -109,14 +103,13 @@ pub async fn create_flow( if input_ref.is_empty() { None } else { - Some(input_ref.iter().map(|x| x.reference()).collect()) + Some(input_ref.iter().map(|x| x.id).collect()) }, ) .await; for t in input_ref { - t.consume(Price::zero(), &format!("flow::{kind}::{}", flow._id)) - .await; + t.consume(0.0, &format!("flow::{kind}::{}", flow.id)).await; } Ok(flow) @@ -139,12 +132,12 @@ pub async fn create_flow_route( flows: &State>, ) -> FallibleApiResponse { let flow = create_flow(id, flows, &form).await?; - Ok(json!({"uuid": flow._id })) + Ok(json!({"uuid": flow.id })) } #[get("/flow/")] pub async fn flow_api_route(id: &str) -> FallibleApiResponse { - let flow = Flow::get(id) + let flow = Flow::get(&uuid::Uuid::from_str(id).map_err(|_| api_error("Invalid UUID"))?) .await .ok_or_else(|| api_error("No such flow"))?; Ok(flow.api().await) @@ -152,11 +145,11 @@ pub async fn flow_api_route(id: &str) -> FallibleApiResponse { #[post("/flow//end", data = "
")] pub async fn end_flow_route(id: &str, form: Json) -> FallibleApiResponse { - let flow = Flow::get(id) + let flow = Flow::get(&uuid::Uuid::from_str(id).map_err(|_| api_error("Invalid UUID"))?) .await .ok_or_else(|| api_error("Flow not found"))?; - if flow.done.is_some() { + if flow.ended.is_some() { return Err(api_error("Flow already ended")); } @@ -175,17 +168,17 @@ pub async fn continue_flow_route( flows: &State>, form: Json, ) -> FallibleApiResponse { - let this_flow = Flow::get(id) + let this_flow = Flow::get(&uuid::Uuid::from_str(id).map_err(|_| api_error("Invalid UUID"))?) .await .ok_or_else(|| api_error("Flow not found"))?; - if this_flow.done.is_some() { + if this_flow.ended.is_some() { return Err(api_error("Flow already ended")); } // create next flow let next_kind = flows - .get(this_flow.kind.id()) + .get(&this_flow.kind) .ok_or_else(|| api_error("Flow not found"))? .next .clone() @@ -195,20 +188,12 @@ pub async fn continue_flow_route( // end current flow this_flow.continue_next(&next_flow).await; - Ok(json!({"uuid": next_flow._id})) + Ok(json!({"uuid": next_flow.id})) } #[get("/flow//notes")] pub async fn flow_notes_route(id: &str) -> FallibleApiResponse { - let notes = FlowNote::find( - doc! { - "on_flow": reference_of!(Flow, id).ok_or_else(|| api_error("No such flow"))? - }, - None, - Some(doc! { "timestamp": Sort::Descending }), - ) - .await - .unwrap(); + let notes = FlowNote::find_of(id).await; Ok(json!(vec_to_api(¬es).await)) } @@ -222,8 +207,8 @@ pub struct NoteAdd { pub async fn create_flow_note_route(id: &str, form: Json) -> FallibleApiResponse { let note = FlowNote::create( &form.content, - reference_of!(Flow, id).ok_or_else(|| api_error("No such flow"))?, + &uuid::Uuid::from_str(id).map_err(|_| api_error("Invalid UUID"))?, ) .await; - Ok(json!({"uuid": note._id })) + Ok(json!({"uuid": note.id })) } diff --git a/src/routes/item/demand.rs b/src/routes/item/demand.rs index af3e168..122feee 100644 --- a/src/routes/item/demand.rs +++ b/src/routes/item/demand.rs @@ -1,14 +1,14 @@ -use mongod::{Model, ToAPI}; +use std::str::FromStr; + use rocket::serde::json::Json; use rocket::{get, post, State}; use serde::Deserialize; use serde_json::json; -use crate::check_auth; use crate::config::{Config, Webhook}; -use crate::item::Item; -use crate::routes::Token; +use crate::routes::{ToAPI, Token}; use crate::variant::Variant; +use crate::{check_auth, get_itemdb}; use crate::{ db::ItemDB, routes::{api_error, FallibleApiResponse}, @@ -20,7 +20,7 @@ use super::{item_does_not_exist_error, variant_does_not_exist_error}; pub struct DemandForm { uuid: String, destination: String, - price: String, + price: f64, } /// Consumes a Transaction with Price and Destination @@ -29,11 +29,8 @@ pub async fn demand_route(f: Json, t: Token, c: &State) -> F check_auth!(t, c); let transaction = Variant::demand( - &f.uuid, - f.price - .clone() - .try_into() - .map_err(|()| api_error("Price malformed"))?, + &uuid::Uuid::from_str(&f.uuid).map_err(|_| api_error("Invalid UUID"))?, + f.price, &f.destination, ) .await @@ -45,8 +42,8 @@ pub async fn demand_route(f: Json, t: Token, c: &State) -> F } if let Some(url) = &hook.item_below_minimum { - let variant = Item::get(&transaction.item) - .await + let variant = get_itemdb!() + .get_item(&transaction.item) .unwrap() .variant(&transaction.variant) .unwrap(); diff --git a/src/routes/item/location.rs b/src/routes/item/location.rs index c2678d5..89adf34 100644 --- a/src/routes/item/location.rs +++ b/src/routes/item/location.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; -use mongod::{vec_to_api, ToAPI}; use rocket::{get, State}; use serde_json::json; @@ -9,7 +8,7 @@ use crate::{ config::Config, json_store::JSONStore, location::Location, - routes::{api_error, FallibleApiResponse, Token}, + routes::{api_error, vec_to_api, FallibleApiResponse, ToAPI, Token}, transaction::Transaction, }; @@ -121,11 +120,6 @@ pub async fn location_inventory( } Ok(json!( - vec_to_api( - &Transaction::in_location(location) - .await - .ok_or_else(|| api_error("No such location"))? - ) - .await + vec_to_api(&Transaction::in_location(location).await).await )) } diff --git a/src/routes/item/mod.rs b/src/routes/item/mod.rs index 6c10427..0985e06 100644 --- a/src/routes/item/mod.rs +++ b/src/routes/item/mod.rs @@ -4,12 +4,11 @@ mod location; mod stat; mod supply; +use std::str::FromStr; + pub use demand::*; pub use error::*; pub use location::*; -use mongod::reference_of; -use mongod::Model; -use mongod::ToAPI; use rocket::post; use rocket::serde::json::Json; use serde::Deserialize; @@ -25,7 +24,9 @@ use crate::check_auth; use crate::config::Config; use crate::db::get_items_without_min_satisfied; use crate::db::ItemDB; -use crate::location::Location; +use crate::get_locations; +use crate::get_pg; +use crate::routes::ToAPI; use crate::routes::Token; use crate::transaction::Transaction; @@ -91,9 +92,11 @@ pub async fn transaction_route( ) -> FallibleApiResponse { check_auth!(t, c); - let t = Transaction::get(transaction) - .await - .ok_or_else(|| api_error("No transaction with this UUID"))?; + let t = Transaction::get( + &uuid::Uuid::from_str(&transaction).map_err(|_| api_error("Invalid UUID"))?, + ) + .await + .ok_or_else(|| api_error("No transaction with this UUID"))?; Ok(t.api().await) } @@ -184,16 +187,16 @@ pub struct MoveTransaction { #[post("/transaction//move", data = "")] pub async fn move_transaction_route(id: &str, form: Json) -> FallibleApiResponse { let new_loc = &form.to; - Transaction::get(id) - .await - .ok_or_else(|| api_error("No such transaction"))? - .change() - .location(if form.to.is_empty() { - None - } else { - Some(reference_of!(Location, new_loc).ok_or_else(|| api_error("No such location"))?) - }) - .update() + let locations = get_locations!(); + + if !locations.contains_key(new_loc) { + return Err(api_error("No such location")); + } + + sqlx::query("UPDATE transactions SET location = $1 WHERE id = $2") + .bind(new_loc) + .bind(uuid::Uuid::from_str(id).map_err(|_| api_error("Invalid UUID"))?) + .execute(get_pg!()) .await .unwrap(); diff --git a/src/routes/item/stat.rs b/src/routes/item/stat.rs index 4da647c..92df6f1 100644 --- a/src/routes/item/stat.rs +++ b/src/routes/item/stat.rs @@ -73,7 +73,7 @@ pub async fn item_stat_route( let item_var = itemdb.get_item(&item).unwrap().variant(var).unwrap(); for t in item_var.inventory().await { transaction_count += 1; - total_price += t.price.value; + total_price += t.price; } } } diff --git a/src/routes/item/supply.rs b/src/routes/item/supply.rs index afbda10..6add059 100644 --- a/src/routes/item/supply.rs +++ b/src/routes/item/supply.rs @@ -1,4 +1,3 @@ -use mongod::ToAPI; use rocket::serde::json::Json; use rocket::{get, post, State}; use serde::{Deserialize, Serialize}; @@ -6,7 +5,7 @@ use serde_json::json; use crate::check_auth; use crate::config::{Config, Webhook}; -use crate::routes::Token; +use crate::routes::{vec_to_api, ToAPI, Token}; use crate::{ db::ItemDB, routes::{api_error, FallibleApiResponse}, @@ -18,7 +17,7 @@ use super::{item_does_not_exist_error, variant_does_not_exist_error}; pub struct SupplyForm { pub item: String, pub variant: String, - pub price: String, + pub price: f64, pub origin: Option, pub location: Option, pub note: Option, @@ -42,10 +41,7 @@ pub async fn supply_route( let transaction = variant .supply( - form.price - .clone() - .try_into() - .map_err(|()| api_error("Price malformed"))?, + form.price, form.origin.as_deref(), form.location.as_deref(), form.note.as_deref(), @@ -58,7 +54,7 @@ pub async fn supply_route( } } - Ok(json!({"uuid": transaction._id})) + Ok(json!({"uuid": transaction.id})) } /// Returns a list of Transaction UUIDs for the Item Variant @@ -104,7 +100,7 @@ pub async fn inventory_route( item.inventory().await }; - Ok(json!(mongod::vec_to_api(&transactions).await)) + Ok(json!(vec_to_api(&transactions).await)) } /// Returns current active Transactions for Item Variant @@ -126,7 +122,7 @@ pub async fn inventory_route_variant( let transactions = variant.inventory().await; - Ok(json!(mongod::vec_to_api(&transactions).await)) + Ok(json!(vec_to_api(&transactions).await)) } /// Returns statistics for the Item Variant diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 19dda34..e185330 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -37,3 +37,20 @@ impl<'r> FromRequest<'r> for Token { } } } + +/// A trait to generate a Model API representation in JSON format. +pub trait ToAPI: Sized { + /// Generate public API JSON + fn api(&self) -> impl std::future::Future; +} + +/// Converts a slice of items implementing the `ToAPI` trait into a `Vec` of JSON values. +pub async fn vec_to_api(items: &[impl ToAPI]) -> Vec { + let mut ret = Vec::with_capacity(items.len()); + + for e in items { + ret.push(e.api().await); + } + + ret +} diff --git a/src/transaction.rs b/src/transaction.rs index e74f3ec..8bfd73f 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,109 +1,85 @@ use futures::StreamExt; -use mongod::{ - assert_reference_of, - derive::{Model, Referencable}, - reference_of, Model, Referencable, Reference, Sort, Validate, -}; -use mongodb::bson::doc; use serde::{Deserialize, Serialize}; use serde_json::json; +use sqlx::prelude::FromRow; -use crate::{item::Item, location::Location}; +use crate::{get_itemdb, get_locations, get_pg, routes::ToAPI}; // todo : produced / consumed by flow field? /// A Transaction of an Item Variant -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Transaction { /// UUID - pub _id: String, + pub id: uuid::Uuid, /// Associated Item pub item: String, /// Associated Variant pub variant: String, /// Price of obtaining the Item - pub price: Price, + pub price: f64, /// Origin of the Item pub origin: Option, /// The location of the Item - pub location: Option, - /// Info on consumption of the Item - pub consumed: Option, + pub location: Option, /// Notes on Transaction pub note: Option, /// Timestamp of the Transaction - pub timestamp: i64, -} - -impl Validate for Transaction { - async fn validate(&self) -> Result<(), String> { - if let Some(location) = &self.location { - assert_reference_of!(location, Location); - } - - Ok(()) - } -} - -/// Information about consumed Items -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct Consumed { + pub created: chrono::DateTime, /// Destination of the Item or who consumed it - pub destination: String, + pub destination: Option, /// Price the Item was exported or consumed at - pub price: Price, + pub consumed_price: Option, /// Timestamp of Consumption - pub timestamp: i64, + pub consumed_timestamp: Option>, } impl Transaction { pub async fn new( item: &str, variant: &str, - price: Price, + price: f64, origin: Option<&str>, location: Option<&str>, note: Option<&str>, ) -> Self { - Self { - _id: uuid::Uuid::new_v4().to_string(), - item: item.to_string(), - variant: variant.to_string(), - price, - consumed: None, - origin: origin.map(std::string::ToString::to_string), - location: if let Some(location) = location { - reference_of!(Location, location) - } else { - None - }, - note: note.map(|x| x.to_string()), - timestamp: chrono::Utc::now().timestamp(), - } + sqlx::query_as("INSERT INTO transactions (item, variant, price, origin, location, note) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *") + .bind(item) + .bind(variant) + .bind(price) + .bind(origin) + .bind(location) + .bind(note) + .fetch_one(get_pg!()).await.unwrap() } - /// Consumes the Item with `price` and `destination` - pub async fn consume(self, price: Price, destination: &str) -> Self { - self.change() - .consumed(Some(Consumed { - destination: destination.to_string(), - price, - timestamp: chrono::Utc::now().timestamp(), - })) - .update() + pub async fn get(id: &uuid::Uuid) -> Option { + sqlx::query_as("SELECT * FROM transactions WHERE id = $1") + .bind(id) + .fetch_optional(get_pg!()) .await .unwrap() } + /// Consumes the Item with `price` and `destination` + pub async fn consume(self, price: f64, destination: &str) -> Self { + sqlx::query_as( +"UPDATE transactions SET consumed_timestamp = current_timestamp, consumed_price = $1, destination = $2 WHERE id = $3 RETURNING *") + .bind(price) + .bind(destination) + .bind(&self.id) + .fetch_one(get_pg!()).await.unwrap() + } + pub async fn is_expired_at(&self, time: i64) -> bool { - if let Some(expiry) = Item::get(&self.item) - .await + if let Some(expiry) = get_itemdb!() + .get_item(&self.item) .unwrap() .variant(&self.variant) .unwrap() .expiry { - let date_added = self.timestamp; + let date_added = self.created.timestamp(); let expiration_ts = expiry * 24 * 60 * 60; @@ -120,15 +96,16 @@ impl Transaction { } pub async fn is_expired(&self) -> bool { - if self.consumed.is_some() { - if let Some(expiry) = Item::get(&self.item) - .await + if self.consumed_timestamp.is_some() { + if let Some(expiry) = get_itemdb!() + .get_item(&self.item) .unwrap() .variant(&self.variant) .unwrap() .expiry { - let time_around = self.timestamp - self.consumed.as_ref().unwrap().timestamp; + let time_around = + self.created.timestamp() - self.consumed_timestamp.unwrap().timestamp(); let expiration_ts = expiry * 24 * 60 * 60; return time_around > expiration_ts; } else { @@ -140,38 +117,24 @@ impl Transaction { self.is_expired_at(current_time).await } - pub async fn in_location(l: &str) -> Option> { - let l = reference_of!(Location, l)?; - Some( - Self::find( - doc! { "location": l, "consumed": { "$not": { "$type": "object" } }}, - None, - None, - ) - .await - .unwrap(), + pub async fn in_location(l: &str) -> Vec { + sqlx::query_as( + "SELECT * FROM transactions WHERE location = $1 AND consumed_timestamp IS NULL", ) + .bind(l) + .fetch_all(get_pg!()) + .await + .unwrap() } pub async fn in_location_recursive(l: &str) -> Option> { // get the children of this location - let locations = Location::get(l).await?.children_recursive().await; + let locations = get_locations!().get(l)?.children_recursive(); - let l = reference_of!(Location, l)?; - let mut transactions = Self::find( - doc! { "location": l, "consumed": { "$not": { "$type": "object" } },}, - None, - None, - ) - .await - .unwrap(); + let mut transactions = Self::in_location(l).await; for loc in locations { - transactions.extend( - Self::find(doc! { "location": loc.reference(), "consumed": { "$not": { "$type": "object" } }}, None, None) - .await - .unwrap(), - ); + transactions.extend(Self::in_location(&loc.id).await); } Some(transactions) @@ -179,13 +142,10 @@ impl Transaction { /// Get all Transactions which are not consumed and are expired pub async fn active_expired(days: Option) -> Vec { - let items = Self::find( - doc! { - "consumed": { "$not": { "$type": "object" } } - }, - None, - Some(doc! { "timestamp": Sort::Descending }), + let items: Vec = sqlx::query_as( + "SELECT * FROM transactions WHERE consumed_timestamp IS NULL ORDER BY created DESC", ) + .fetch_all(get_pg!()) .await .unwrap(); @@ -210,67 +170,35 @@ impl Transaction { } } -impl mongod::ToAPI for Transaction { +impl ToAPI for Transaction { async fn api(&self) -> serde_json::Value { let location = if let Some(loc) = &self.location { - Some(loc.get::().await.api().await) + Some(get_locations!().get(loc).unwrap().api().await) + } else { + None + }; + + let consumed = if self.consumed_timestamp.is_some() { + Some(json!({ + "destination": self.destination, + "price": self.consumed_price, + "timestamp": self.consumed_timestamp.unwrap().timestamp() + })) } else { None }; json!({ - "uuid": self._id, + "uuid": self.id, "item": self.item, "variant": self.variant, "price": self.price, "origin": self.origin, "location": location, - "timestamp": self.timestamp, - "consumed": self.consumed, + "timestamp": self.created.timestamp(), + "consumed": consumed, "note": self.note, "expired": self.is_expired().await }) } } - -/// Economic Price -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct Price { - /// Value of the currency - pub value: f64, - /// Kind of currency - pub currency: String, -} - -impl Price { - pub fn new(value: f64, currency: &str) -> Self { - Self { - value, - currency: currency.to_string(), - } - } - - pub fn zero() -> Self { - Self { - value: 0.00, - currency: String::new(), - } - } - - fn parse(price: &str) -> Option { - let (value, currency) = price.split_once(' ')?; - - Some(Self { - value: value.parse().ok()?, - currency: currency.to_string(), - }) - } -} - -impl TryFrom for Price { - type Error = (); - - fn try_from(value: String) -> Result { - Self::parse(&value).ok_or(()) - } -} diff --git a/src/variant.rs b/src/variant.rs index 31bbeee..03f3dc5 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -1,16 +1,9 @@ use std::collections::HashMap; -use futures::StreamExt; -use mongod::{Model, Sort}; -use mongodb::bson::doc; use serde::{Deserialize, Serialize}; use serde_json::json; -use crate::transaction::{Price, Transaction}; - -pub fn sort_by_timestamp() -> mongodb::bson::Document { - doc! { "timestamp": mongod::Sort::Descending } -} +use crate::{get_pg, transaction::Transaction}; pub fn timestamp_range(year: i32, month: u32) -> (i64, i64) { let d = chrono::NaiveDate::from_ymd_opt(year, month, 0).unwrap(); @@ -85,69 +78,51 @@ impl Variant { /// Returns the IDs of Transactions from this Item Variant. pub async fn supply_log(&self) -> Vec { - let filter = doc! { - "item": &self.item, - "variant": &self.variant - }; + let res: Vec<(uuid::Uuid,)> = sqlx::query_as( + "SELECT id FROM transactions WHERE item = $1 AND variant = $2 ORDER BY created DESC", + ) + .bind(&self.item) + .bind(&self.variant) + .fetch_all(get_pg!()) + .await + .unwrap(); - let result = Transaction::find_partial(filter, json!({}), None, None) - .await - .unwrap(); - - let mut ret = Vec::new(); - - for doc in result { - ret.push(doc._id); - } - - ret + res.into_iter().map(|x| x.0.to_string()).collect() } /// Returns the active Transaction of this Item Variant which are not yet consumed. pub async fn inventory(&self) -> Vec { - let filter = doc! { - "item": &self.item, - "variant": &self.variant, - "consumed": { "$not": { "$type": "object" } } - }; - - Transaction::find(filter, None, None).await.unwrap() + sqlx::query_as("SELECT * FROM transactions WHERE item = $1 AND variant = $2 AND consumed_timestamp IS NULL ORDER BY created DESC") + .bind(&self.item) + .bind(&self.variant) + .fetch_all(get_pg!()).await.unwrap() } /// Returns the IDs of the Transactions from this Item Variant which are consumed. pub async fn demand_log(&self, destination: Option<&str>) -> Vec { - let filter = if let Some(dest) = destination { - doc! { - "item": &self.item, - "variant": &self.variant, - "consumed": { "destination": dest } - } + let res: Vec<(uuid::Uuid,)> = if let Some(destination) = destination { + sqlx::query_as( + "SELECT id FROM transactions WHERE item = $1 AND variant = $2 AND consumed_timestamp IS NOT NULL AND destination = $3 ORDER BY created DESC" + ) + .bind(&self.item) + .bind(&self.variant) + .bind(destination) + .fetch_all(get_pg!()).await.unwrap() } else { - doc! { - "item": &self.item, - "variant": &self.variant, - "consumed": { "$type": "object" } - } + sqlx::query_as( + "SELECT id FROM transactions WHERE item = $1 AND variant = $2 AND consumed_timestamp IS NOT NULL ORDER BY created DESC" + ) + .bind(&self.item) + .bind(&self.variant) + .fetch_all(get_pg!()).await.unwrap() }; - - let result = Transaction::find_partial(filter, json!({}), None, None) - .await - .unwrap(); - - let mut ret = Vec::new(); - - for doc in result { - ret.push(doc._id); - } - - ret + res.into_iter().map(|x| x.0.to_string()).collect() } - pub async fn demand(uuid: &str, price: Price, destination: &str) -> Option { + pub async fn demand(uuid: &uuid::Uuid, price: f64, destination: &str) -> Option { // check if transaction exists - let mut t = Transaction::get(uuid).await?; - t = t.consume(price, destination).await; - Some(t) + let t = Transaction::get(uuid).await?; + Some(t.consume(price, destination).await) } /// Records a supply transaction in the database. @@ -162,107 +137,80 @@ impl Variant { /// Returns a UUID string representing the transaction. pub async fn supply( &self, - price: Price, + price: f64, origin: Option<&str>, location: Option<&str>, note: Option<&str>, ) -> Transaction { - let t = Transaction::new(&self.item, &self.variant, price, origin, location, note).await; - - t.insert().await.unwrap(); - - t + Transaction::new(&self.item, &self.variant, price, origin, location, note).await } /// Returns all Transactions of this Item Variant pub async fn get_all_transactions(&self) -> Vec { - let filter = doc! { - "item": &self.item, - "variant": &self.variant - }; - - Transaction::find(filter, None, Some(doc! { "timestamp": Sort::Descending })) - .await - .unwrap() + sqlx::query_as( + "SELECT * FROM transactions WHERE item = $1 AND variant = $2 ORDER BY created DESC", + ) + .bind(&self.item) + .bind(&self.variant) + .fetch_all(get_pg!()) + .await + .unwrap() } pub async fn get_transaction_timeslice(&self, year: i32, month: u32) -> Vec { let (start, end) = timestamp_range(year, month); - Transaction::find( - doc! { - "timestamp": { - "$gte": start, - "$lte": end - } - }, - None, - Some(sort_by_timestamp()), - ) - .await - .unwrap() + sqlx::query_as("SELECT * FROM transactions WHERE created BETWEEN to_timestamp($1) AND to_timestamp($2) ORDER BY created DESC") + .bind(start) + .bind(end) + .fetch_all(get_pg!()).await.unwrap() } pub async fn get_unique_origins(&self) -> Vec { - unique_flows( - &Transaction::unique( - doc! { - "item": &self.item, - "variant": &self.variant - }, - "origin", - ) - .await, - ) + let res: Vec<(String,)> = sqlx::query_as("SELECT DISTINCT(origin) FROM transactions WHERE origin NOT LIKE 'flow::%' AND item = $1 AND variant = $2") + .bind(&self.item) + .bind(&self.variant) + .fetch_all(get_pg!()).await.unwrap(); + res.into_iter().map(|x| x.0).collect() } pub async fn get_unique_destinations(&self) -> Vec { - unique_flows( - &Transaction::unique( - doc! { - "item": &self.item, - "variant": &self.variant - }, - "consumed.destination", - ) - .await, - ) + let res: Vec<(String,)> = sqlx::query_as("SELECT DISTINCT(destination) FROM transactions WHERE destination NOT LIKE 'flow::%' AND item = $1 AND variant = $2") + .bind(&self.item) + .bind(&self.variant) + .fetch_all(get_pg!()).await.unwrap(); + res.into_iter().map(|x| x.0).collect() } - pub async fn price_history_by_origin(&self, origin: &str, limit: Option) -> Vec { - Transaction::find( - doc! { - "item": &self.item, - "variant": &self.variant, - "origin": origin - }, - limit, - Some(sort_by_timestamp()), + pub async fn price_history_by_origin(&self, origin: &str, limit: Option) -> Vec { + let res: Vec<(f64,)> = sqlx::query_as( + &format!("SELECT price FROM transactions WHERE item = $1 AND variant = $2 AND origin = $3 ORDER BY created DESC {}", if let Some(limit) = limit { + format!("LIMIT {limit}") + } else { String::new() }) ) - .await - .unwrap() - .into_iter() - .map(|x| x.price) - .collect() + .bind(&self.item) + .bind(&self.variant) + .bind(origin) + .fetch_all(get_pg!()).await.unwrap(); + res.into_iter().map(|x| x.0).collect() } - pub async fn get_latest_price(&self, origin: Option) -> Price { - let mut filter = doc! { - "item": &self.item, - "variant": &self.variant - }; - + pub async fn get_latest_price(&self, origin: Option) -> f64 { if let Some(origin) = origin { - filter.insert("origin", origin); + let res: (f64,) = sqlx::query_as("SELECT price FROM transactions WHERE item = $1 AND variant = $2 AND origin = $3 ORDER BY created DESC LIMIT 1") + .bind(&self.item) + .bind(&self.variant) + .bind(origin) + .fetch_one(get_pg!()).await.unwrap(); + res.0 + } else { + let res: (f64,) = sqlx::query_as("SELECT price FROM transactions WHERE item = $1 AND variant = $2 ORDER BY created DESC LIMIT 1") + .bind(&self.item) + .bind(&self.variant) + .bind(origin) + .fetch_one(get_pg!()).await.unwrap(); + res.0 } - - Transaction::find(filter, Some(1), Some(sort_by_timestamp())) - .await - .unwrap() - .first() - .unwrap() - .price - .clone() } /// Check if item variant is below minimum. Returns if this is the case and the number needed to fulfill minimum @@ -280,8 +228,7 @@ impl Variant { pub async fn stat(&self, full: bool) -> serde_json::Value { let active_transactions = self.inventory().await; - // fix : ignores currency - let total_price: f64 = active_transactions.iter().map(|x| x.price.value).sum(); + let total_price: f64 = active_transactions.iter().map(|x| x.price).sum(); if !full { return json!({ @@ -290,13 +237,15 @@ impl Variant { }); } - let all_transactions = Transaction::find( - doc! { "item": &self.item, "variant": &self.variant}, - None, - None, + let all_transactions: Vec = sqlx::query_as( + "SELECT * FROM transactions WHERE item = $1 AND variant = $2 ORDER BY created DESC", ) + .bind(&self.item) + .bind(&self.variant) + .fetch_all(get_pg!()) .await .unwrap(); + let mut expired_count = 0.0; for t in &all_transactions { @@ -318,7 +267,7 @@ impl Variant { .price_history_by_origin(&origin, None) .await .into_iter() - .map(|x| x.value) + .map(|x| x) .collect::>(); let prices_len = prices.len() as f64; let prices_summed = prices.into_iter().reduce(|acc, e| acc + e).unwrap_or(0.0); @@ -349,29 +298,3 @@ impl Variant { }) } } - -pub fn unique_flows(i: &[String]) -> Vec { - let mut unique_vec: Vec = Vec::new(); - - for s in i { - // Check if the string starts with "flow::" - if let Some(suffix) = s.strip_prefix("flow::") { - // Extract the part after "flow::" and split on "::" to get the kind (ignoring id) - let parts: Vec<&str> = suffix.split("::").collect(); - if let Some(kind) = parts.first() { - // Build the common prefix "flow::kind" - let common_prefix = format!("flow::{}", kind); - - if !unique_vec.contains(&common_prefix) { - unique_vec.push(common_prefix); - } - } - } else { - // If the string doesn't start with "flow::", retain it - // Assumption: Except "flow::" values, everything should be already unique - unique_vec.push(s.to_string()); - } - } - - unique_vec -}