diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b1fd67f..1184784e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ name: CI -on: [push] - +on: + push: env: CARGO_TERM_COLOR: always FORCE_COLOR: true @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v2 - - name: Install nightly + - name: Install nightly and clippy uses: actions-rs/toolchain@v1 with: profile: minimal @@ -71,6 +71,10 @@ jobs: with: command: run args: --bin gen_contracts + - name: Install NPM + uses: actions/setup-node@v3 + with: + node-version: 18.3 - name: Install run: npm ci --dev - name: Build diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 15285978..f27afb01 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,22 +1,6 @@ { "version": "2.0.0", "tasks": [ - { - "label": "Tslint", - "type": "shell", - "command": "${workspaceFolder}/scripts/lint.sh", - "args": [ - "typescript" - ], - "group": "test", - "presentation": { - "echo": true, - "reveal": "always", - "focus": false, - "panel": "dedicated", - "showReuseMessage": false - } - }, { "type": "npm", "script": "watch", @@ -54,6 +38,11 @@ "script": "analyze-bundle", "path": "web/", "problemMatcher": [] + },{ + "type": "npm", + "script": "test", + "path": "web/", + "problemMatcher": [] }, { "label": "generate contracts", @@ -98,10 +87,10 @@ } }, { - "label": "build", - "type": "shell", - "command": "cargo", - "args": ["build"], + "label": "build server", + "type": "cargo", + "command": "build", + "args": [], "group": "build", "problemMatcher": "$rustc", "presentation": { diff --git a/Cargo.lock b/Cargo.lock index f07e1dfc..153a9e8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.17.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -17,37 +17,32 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "aead" -version = "0.3.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "generic-array 0.14.5", + "crypto-common", + "generic-array", ] [[package]] name = "aes" -version = "0.6.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "aes-soft", - "aesni", + "cfg-if 1.0.0", "cipher", + "cpufeatures", ] [[package]] name = "aes-gcm" -version = "0.8.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", @@ -58,374 +53,252 @@ dependencies = [ ] [[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aesni" -version = "0.10.0" +name = "ahash" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "cipher", - "opaque-debug 0.3.0", + "getrandom", + "once_cell", + "version_check", ] -[[package]] -name = "ahash" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" - [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] -name = "async-channel" -version = "1.6.1" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] -name = "async-executor" -version = "1.4.1" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", - "slab", + "libc", ] [[package]] -name = "async-global-executor" -version = "2.0.4" +name = "async-stream" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c290043c9a95b05d45e952fb6383c67bcb61471f60cfa21e890dba6654234f43" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-mutex", - "blocking", - "futures-lite", - "num_cpus", - "once_cell", + "async-stream-impl", + "futures-core", + "pin-project-lite", ] [[package]] -name = "async-io" -version = "1.6.0" +name = "async-stream-impl" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "concurrent-queue", - "futures-lite", - "libc", - "log 0.4.16", - "once_cell", - "parking", - "polling", - "slab", - "socket2 0.4.4", - "waker-fn", - "winapi 0.3.9", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] -name = "async-lock" -version = "2.5.0" +name = "async-trait" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "event-listener", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] -name = "async-mutex" -version = "1.4.0" +name = "atomic" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener", -] +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" [[package]] -name = "async-std" -version = "1.11.0" +name = "atomic" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" dependencies = [ - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log 0.4.16", - "memchr", - "num_cpus", - "once_cell", - "pin-project-lite 0.2.8", - "pin-utils", - "slab", - "wasm-bindgen-futures", + "bytemuck", ] -[[package]] -name = "async-task" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" - -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - [[package]] name = "attohttpc" -version = "0.15.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe174d1b67f7b2bafed829c09db039301eb5841f66e43be2cf60b326e7f8e2cc" +checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" dependencies = [ - "http", - "log 0.4.16", + "http 0.2.12", + "log", "native-tls", - "openssl", "serde", "serde_json", - "url 2.2.2", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi 0.3.9", + "url", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-creds" -version = "0.24.1" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad53a54cb2c99990e96eefacde6f143dc6d471ab70809d26d360292be421d490" +checksum = "3776743bb68d4ad02ba30ba8f64373f1be4e082fe47651767171ce75bb2f6cf5" dependencies = [ "attohttpc", "dirs", + "log", + "quick-xml", "rust-ini", "serde", - "serde-xml-rs", - "serde_derive", - "simpl", - "url 2.2.2", + "thiserror", + "time", + "url", ] [[package]] name = "aws-region" -version = "0.22.1" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f610af4a396f07592014dc3410f6ad78fab931852a99bb6cfdc1ad04b9329b80" +checksum = "42fed2b9fca70f2908268d057a607f2a906f47edbf856ea8587de9038d264e22" dependencies = [ - "simpl", + "thiserror", ] [[package]] name = "backtrace" -version = "0.3.64" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.4.4", + "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "base64" -version = "0.9.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -dependencies = [ - "byteorder", - "safemem", -] +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.13.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bcrypt" -version = "0.9.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d0faafe9e089674fc3efdb311ff5253d445c79d85d1d28bd3ace76d45e7164" +checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7" dependencies = [ - "base64 0.13.0", + "base64 0.22.1", "blowfish", - "getrandom 0.2.6", + "getrandom", + "subtle", + "zeroize", ] [[package]] -name = "bitflags" -version = "1.3.2" +name = "binascii" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" [[package]] -name = "block-buffer" -version = "0.7.3" +name = "bit_field" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] -name = "block-buffer" -version = "0.9.0" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.5", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "block-padding" -version = "0.1.5" +name = "bitflags" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] -name = "blocking" -version = "1.2.0" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", + "generic-array", ] [[package]] name = "blowfish" -version = "0.7.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fa6a061124e37baba002e496d203e23ba3d7b73750be82dbfbc92913048a5b" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" dependencies = [ "byteorder", "cipher", - "opaque-debug 0.3.0", -] - -[[package]] -name = "buf_redux" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" -dependencies = [ - "memchr", - "safemem", ] [[package]] name = "bumpalo" -version = "3.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" - -[[package]] -name = "byte-tools" -version = "0.3.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.9.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cache-padded" -version = "1.2.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -441,25 +314,27 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ - "libc", - "num-integer", + "android-tzdata", + "iana-time-zone", + "js-sys", "num-traits", "serde", - "time", - "winapi 0.3.9", + "wasm-bindgen", + "windows-targets 0.52.5", ] [[package]] name = "cipher" -version = "0.2.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array 0.14.5", + "crypto-common", + "inout", ] [[package]] @@ -468,36 +343,28 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "concurrent-queue" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" -dependencies = [ - "cache-padded", -] - [[package]] name = "cookie" -version = "0.11.4" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f6044740a4a516b8aac14c140cdf35c1a640b1bd6b98b6224e49143b2f1566" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ "aes-gcm", - "base64 0.13.0", + "base64 0.22.1", "hkdf", - "hmac 0.10.1", - "percent-encoding 2.1.0", - "rand 0.8.5", + "percent-encoding", + "rand", "sha2", + "subtle", "time", + "version_check", ] [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -505,133 +372,116 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - [[package]] name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.8" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if 1.0.0", "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if 1.0.0", - "lazy_static", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] -name = "crypto-mac" -version = "0.9.1" +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.5", - "subtle", + "generic-array", + "rand_core", + "typenum", ] [[package]] -name = "crypto-mac" -version = "0.10.1" +name = "ctr" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "generic-array 0.14.5", - "subtle", + "cipher", ] [[package]] -name = "ctor" -version = "0.1.22" +name = "deadpool" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" dependencies = [ - "quote 1.0.17", - "syn 1.0.90", + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", ] [[package]] -name = "ctr" -version = "0.6.0" +name = "deadpool-runtime" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" dependencies = [ - "cipher", + "tokio", ] [[package]] -name = "deflate" -version = "0.8.6" +name = "deranged" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "adler32", - "byteorder", + "powerfmt", + "serde", ] [[package]] name = "devise" -version = "0.2.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e04ba2d03c5fa0d954c061fc8c9c288badadffc272ebb87679a89846de3ed3" +checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8" dependencies = [ "devise_codegen", "devise_core", @@ -639,90 +489,110 @@ dependencies = [ [[package]] name = "devise_codegen" -version = "0.2.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066ceb7928ca93a9bedc6d0e612a8a0424048b0ab1f75971b203d01420c055d7" +checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6" dependencies = [ "devise_core", - "quote 0.6.13", + "quote 1.0.36", ] [[package]] name = "devise_core" -version = "0.2.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf41c59b22b5e3ec0ea55c7847e5f358d340f3a8d6d53a5cf4f1564967f96487" +checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" dependencies = [ - "bitflags", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "bitflags 2.5.0", + "proc-macro2 1.0.85", + "proc-macro2-diagnostics", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] name = "diesel" -version = "1.4.8" +version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" +checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" dependencies = [ - "bitflags", + "bitflags 2.5.0", "byteorder", "chrono", "diesel_derives", + "itoa", "pq-sys", - "r2d2", +] + +[[package]] +name = "diesel-async" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be" +dependencies = [ + "async-trait", + "deadpool", + "diesel", + "futures-util", + "scoped-futures", + "tokio", + "tokio-postgres", ] [[package]] name = "diesel_derives" -version = "1.4.1" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "diesel_table_macro_syntax", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] name = "diesel_migrations" -version = "1.4.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" +checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" dependencies = [ + "diesel", "migrations_internals", "migrations_macros", ] [[package]] -name = "difference" -version = "2.0.0" +name = "diesel_table_macro_syntax" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +dependencies = [ + "syn 2.0.66", +] [[package]] -name = "digest" -version = "0.8.1" +name = "difflib" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "digest" -version = "0.9.0" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "generic-array 0.14.5", + "block-buffer", + "crypto-common", + "subtle", ] [[package]] name = "dirs" -version = "3.0.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ "dirs-sys", ] @@ -735,44 +605,78 @@ checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", - "winapi 0.3.9", + "winapi", ] [[package]] -name = "dlv-list" -version = "0.2.3" +name = "displaydoc" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "rand 0.8.5", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + [[package]] name = "downcast" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "either" -version = "1.6.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "encoding_rs" -version = "0.8.30" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if 1.0.0", ] [[package]] -name = "event-listener" -version = "2.5.2" +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "exr" +version = "1.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] [[package]] name = "failure" @@ -790,48 +694,75 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", - "synstructure", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", + "synstructure 0.12.6", ] [[package]] -name = "fake-simd" -version = "0.1.2" +name = "fallible-iterator" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.7.0" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fdeflate" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ - "instant", + "simd-adler32", ] [[package]] -name = "filetime" -version = "0.2.15" +name = "figment" +version = "0.10.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall", - "winapi 0.3.9", + "atomic 0.6.0", + "pear", + "serde", + "toml 0.8.14", + "uncased", + "version_check", +] + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", ] [[package]] name = "float-cmp" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ "num-traits", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -855,60 +786,24 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] name = "fragile" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d758e60b45e8d749c89c1b389ad8aee550f86aa12e2b9298b546dda7a82ab1" - -[[package]] -name = "fsevent" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" -dependencies = [ - "bitflags", - "fsevent-sys", -] - -[[package]] -name = "fsevent-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" -dependencies = [ - "libc", -] - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "futures" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -921,9 +816,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -931,15 +826,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -948,53 +843,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-lite" -version = "1.12.0" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite 0.2.8", - "waker-fn", -] +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1003,67 +883,60 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.8", + "pin-project-lite", "pin-utils", "slab", ] [[package]] -name = "generic-array" -version = "0.12.4" +name = "generator" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" dependencies = [ - "typenum", + "cc", + "libc", + "log", + "rustversion", + "windows", ] [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.4", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", + "version_check", ] [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "ghash" -version = "0.3.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ - "opaque-debug 0.3.0", + "opaque-debug", "polyval", ] [[package]] name = "gif" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -1071,72 +944,65 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "gloo-timers" -version = "0.2.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.2.7" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ - "bytes 0.5.6", + "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", "tokio-util", "tracing", - "tracing-futures", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if 1.0.0", + "crunchy", ] [[package]] name = "hashbrown" -version = "0.7.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", - "autocfg", ] [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1146,104 +1012,85 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" -version = "0.10.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "digest 0.9.0", - "hmac 0.10.1", + "hmac", ] [[package]] name = "hmac" -version = "0.9.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "crypto-mac 0.9.1", - "digest 0.9.0", + "digest", ] [[package]] -name = "hmac" -version = "0.10.1" +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "crypto-mac 0.10.1", - "digest 0.9.0", + "bytes", + "fnv", + "itoa", ] [[package]] name = "http" -version = "0.2.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ - "bytes 1.1.0", + "bytes", "fnv", - "itoa 1.0.1", + "itoa", ] [[package]] name = "http-body" -version = "0.3.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "bytes 0.5.6", - "http", + "bytes", + "http 0.2.12", + "pin-project-lite", ] [[package]] name = "httparse" -version = "1.6.0" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" +checksum = "9f3935c160d00ac752e09787e6e6bfc26494c2183cc922f1bc678a60d4733bc2" [[package]] name = "httpdate" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" - -[[package]] -name = "hyper" -version = "0.10.16" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" -dependencies = [ - "base64 0.9.3", - "httparse", - "language-tags", - "log 0.3.9", - "mime 0.2.6", - "num_cpus", - "time", - "traitobject", - "typeable", - "unicase 1.4.2", - "url 1.7.2", -] +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.13.10" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ - "bytes 0.5.6", + "bytes", "futures-channel", "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "httparse", "httpdate", - "itoa 0.4.8", - "pin-project", - "socket2 0.3.19", + "itoa", + "pin-project-lite", + "socket2", "tokio", "tower-service", "tracing", @@ -1252,37 +1099,178 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 0.5.6", - "hyper 0.13.10", + "bytes", + "hyper", "native-tls", "tokio", - "tokio-tls", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] name = "idna" -version = "0.1.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "idna" -version = "0.2.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -1293,164 +1281,152 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" [[package]] name = "image" -version = "0.23.14" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", "color_quant", + "exr", "gif", "jpeg-decoder", - "num-iter", - "num-rational", "num-traits", "png", - "scoped_threadpool", + "qoi", "tiff", ] [[package]] name = "indexmap" -version = "1.8.1" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ - "autocfg", - "hashbrown 0.11.2", + "equivalent", + "hashbrown 0.14.5", + "serde", ] [[package]] -name = "inotify" -version = "0.7.1" +name = "inlinable_string" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" -dependencies = [ - "bitflags", - "inotify-sys", - "libc", -] +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" [[package]] -name = "inotify-sys" -version = "0.1.5" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "libc", + "generic-array", ] [[package]] -name = "instant" -version = "0.1.12" +name = "ipnet" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] -name = "iovec" -version = "0.1.4" +name = "is-terminal" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ + "hermit-abi", "libc", + "windows-sys 0.52.0", ] [[package]] -name = "ipnet" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" - -[[package]] -name = "itoa" -version = "0.4.8" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jpeg-decoder" -version = "0.1.22" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" dependencies = [ "rayon", ] [[package]] name = "js-sys" -version = "0.3.56" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "kamadak-exif" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70494964492bf8e491eb3951c5d70c9627eb7100ede6cc56d748b9a3f302cfb6" +checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077" dependencies = [ "mutate_once", ] [[package]] -name = "kernel32-sys" -version = "0.2.2" +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "kv-log-macro" -version = "1.0.7" +name = "lebe" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log 0.4.16", -] +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] -name = "language-tags" -version = "0.2.2" +name = "libc" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] -name = "lazy_static" -version = "1.4.0" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] [[package]] -name = "lazycell" -version = "1.3.0" +name = "linux-raw-sys" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] -name = "libc" -version = "0.2.121" +name = "litemap" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1458,169 +1434,129 @@ dependencies = [ [[package]] name = "log" -version = "0.3.9" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -dependencies = [ - "log 0.4.16", -] +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] -name = "log" -version = "0.4.16" +name = "loom" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" dependencies = [ "cfg-if 1.0.0", - "value-bag", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", ] [[package]] -name = "maplit" -version = "1.0.2" +name = "matchers" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] [[package]] -name = "matches" -version = "0.1.9" +name = "maybe-async" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] [[package]] -name = "md5" -version = "0.7.0" +name = "md-5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if 1.0.0", + "digest", +] [[package]] -name = "memchr" -version = "2.4.1" +name = "md5" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] -name = "memoffset" -version = "0.6.5" +name = "memchr" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "migrations_internals" -version = "1.4.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" +checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" dependencies = [ - "diesel", + "serde", + "toml 0.7.8", ] [[package]] name = "migrations_macros" -version = "1.4.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" +checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" dependencies = [ "migrations_internals", - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", -] - -[[package]] -name = "mime" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -dependencies = [ - "log 0.3.9", + "proc-macro2 1.0.85", + "quote 1.0.36", ] [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "mime_guess" -version = "2.0.4" +name = "minidom" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f45614075738ce1b77a1768912a60c0227525971b03e09122a05b8a34a2a6278" dependencies = [ - "mime 0.3.16", - "unicase 2.6.0", + "rxml", ] [[package]] name = "miniz_oxide" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -dependencies = [ - "adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", - "autocfg", + "simd-adler32", ] [[package]] name = "mio" -version = "0.6.23" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", "libc", - "log 0.4.16", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] - -[[package]] -name = "mio-extras" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" -dependencies = [ - "lazycell", - "log 0.4.16", - "mio", - "slab", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", + "wasi", + "windows-sys 0.48.0", ] [[package]] name = "mockall" -version = "0.9.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d614ad23f9bb59119b8b5670a85c7ba92c5e9adf4385c81ea00c51c8be33d5" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if 1.0.0", "downcast", @@ -1633,32 +1569,33 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.9.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd4234635bca06fc96c7368d038061e0aae1b00a764dc817e900dc974e3deea" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if 1.0.0", - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", ] [[package]] -name = "multipart" -version = "0.17.1" +name = "multer" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050aeedc89243f5347c3e237e3e13dc76fbe4ae3742a57b94dc14f69acf76d4" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" dependencies = [ - "buf_redux", + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", "httparse", - "log 0.4.16", - "mime 0.3.16", - "mime_guess", - "quick-error", - "rand 0.7.3", - "safemem", - "tempfile", - "twoway", + "memchr", + "mime", + "spin", + "tokio", + "tokio-util", + "version_check", ] [[package]] @@ -1669,13 +1606,12 @@ checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", - "log 0.4.16", + "log", "openssl", "openssl-probe", "openssl-sys", @@ -1685,17 +1621,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "net2" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", -] - [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -1703,69 +1628,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] -name = "notify" -version = "4.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" -dependencies = [ - "bitflags", - "filetime", - "fsevent", - "fsevent-sys", - "inotify", - "libc", - "mio", - "mio-extras", - "walkdir", - "winapi 0.3.9", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.42" +name = "nu-ansi-term" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ - "autocfg", - "num-integer", - "num-traits", + "overload", + "winapi", ] [[package]] -name = "num-rational" -version = "0.3.2" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", @@ -1773,45 +1664,51 @@ dependencies = [ [[package]] name = "object" -version = "0.27.1" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" - -[[package]] -name = "opaque-debug" -version = "0.2.3" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.38" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags", + "bitflags 2.5.0", "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", + "openssl-macros", "openssl-sys", ] +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -1820,11 +1717,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.72" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -1833,93 +1729,88 @@ dependencies = [ [[package]] name = "ordered-multimap" -version = "0.2.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88f947c6799d5eff50e6cf8a2365c17ac4aa8f8f43aceeedc29b616d872a358" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ "dlv-list", - "hashbrown 0.7.2", + "hashbrown 0.12.3", ] [[package]] -name = "parking" -version = "2.0.0" +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ - "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", - "instant", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "winapi 0.3.9", + "windows-targets 0.52.5", ] [[package]] name = "pear" -version = "0.1.4" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5320f212db967792b67cfe12bd469d08afd6318a249bd917d5c19bc92200ab8a" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" dependencies = [ + "inlinable_string", "pear_codegen", + "yansi", ] [[package]] name = "pear_codegen" -version = "0.1.4" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc1c836fdc3d1ef87c348b237b5b5c4dff922156fb2d968f57734f9669768ca" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", - "version_check 0.9.4", - "yansi", + "proc-macro2 1.0.85", + "proc-macro2-diagnostics", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - -[[package]] -name = "percent-encoding" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.1.3" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ + "memchr", + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -1927,59 +1818,51 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1", + "sha2", ] [[package]] -name = "pin-project" -version = "1.0.10" +name = "phf" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "pin-project-internal", + "phf_shared", ] [[package]] -name = "pin-project-internal" -version = "1.0.10" +name = "phf_shared" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "siphasher", ] [[package]] name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - -[[package]] -name = "pin-project-lite" -version = "0.2.8" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1989,69 +1872,94 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "png" -version = "0.16.8" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", - "deflate", - "miniz_oxide 0.3.7", + "fdeflate", + "flate2", + "miniz_oxide", ] [[package]] -name = "polling" -version = "2.2.0" +name = "polyval" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if 1.0.0", - "libc", - "log 0.4.16", - "wepoll-ffi", - "winapi 0.3.9", + "cpufeatures", + "opaque-debug", + "universal-hash", ] [[package]] -name = "polyval" -version = "0.4.5" +name = "postgres-protocol" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" dependencies = [ - "cpuid-bool", - "opaque-debug 0.3.0", - "universal-hash", + "base64 0.21.7", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand", + "sha2", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pq-sys" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda" +checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" dependencies = [ "vcpkg", ] [[package]] name = "predicates" -version = "1.0.8" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ - "difference", + "difflib", "float-cmp", + "itertools", "normalize-line-endings", "predicates-core", "regex", @@ -2059,15 +1967,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -2080,10 +1988,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", - "version_check 0.9.4", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", + "version_check", ] [[package]] @@ -2092,9 +2000,9 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "version_check 0.9.4", + "proc-macro2 1.0.85", + "quote 1.0.36", + "version_check", ] [[package]] @@ -2108,59 +2016,61 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ - "unicode-xid 0.2.2", + "unicode-ident", ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "proc-macro2-diagnostics" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "version_check", + "yansi", +] [[package]] -name = "quote" -version = "0.6.13" +name = "qoi" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" dependencies = [ - "proc-macro2 0.4.30", + "bytemuck", ] [[package]] -name = "quote" -version = "1.0.17" +name = "quick-xml" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" dependencies = [ - "proc-macro2 1.0.36", + "memchr", + "serde", ] [[package]] -name = "r2d2" -version = "0.8.9" +name = "quote" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ - "log 0.4.16", - "parking_lot", - "scheduled-thread-pool", + "proc-macro2 0.4.30", ] [[package]] -name = "rand" -version = "0.7.3" +name = "quote" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", + "proc-macro2 1.0.85", ] [[package]] @@ -2170,361 +2080,434 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_chacha", + "rand_core", ] [[package]] name = "rand_chacha" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.5.1", + "rand_core", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "ppv-lite86", - "rand_core 0.6.3", + "getrandom", ] [[package]] -name = "rand_core" -version = "0.5.1" +name = "rayon" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ - "getrandom 0.1.16", + "either", + "rayon-core", ] [[package]] -name = "rand_core" -version = "0.6.3" +name = "rayon-core" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "getrandom 0.2.6", + "crossbeam-deque", + "crossbeam-utils", ] [[package]] -name = "rand_hc" -version = "0.2.0" +name = "redox_syscall" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "rand_core 0.5.1", + "bitflags 1.3.2", ] [[package]] -name = "rayon" -version = "1.5.1" +name = "redox_syscall" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", + "bitflags 2.5.0", ] [[package]] -name = "rayon-core" -version = "1.9.1" +name = "redox_users" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", + "getrandom", + "libredox", + "thiserror", ] [[package]] -name = "redox_syscall" -version = "0.2.13" +name = "ref-cast" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ - "bitflags", + "ref-cast-impl", ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "ref-cast-impl" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ - "getrandom 0.2.6", - "redox_syscall", - "thiserror", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] -name = "regex-syntax" -version = "0.6.25" +name = "regex-automata" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-automata" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ - "winapi 0.3.9", + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "reqwest" -version = "0.10.10" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.13.0", - "bytes 0.5.6", + "base64 0.21.7", + "bytes", "encoding_rs", "futures-core", "futures-util", - "http", + "h2", + "http 0.2.12", "http-body", - "hyper 0.13.10", + "hyper", "hyper-tls", "ipnet", "js-sys", - "lazy_static", - "log 0.4.16", - "mime 0.3.16", - "mime_guess", + "log", + "mime", "native-tls", - "percent-encoding 2.1.0", - "pin-project-lite 0.2.8", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", + "system-configuration", "tokio", - "tokio-tls", - "url 2.2.2", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "winreg", ] +[[package]] +name = "retain_mut" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" + [[package]] name = "rocket" -version = "0.4.10" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a7ab1dfdc75bb8bd2be381f37796b1b300c45a3c9145b34d86715e8dd90bf28" +checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" dependencies = [ - "atty", - "base64 0.13.0", - "log 0.4.16", + "async-stream", + "async-trait", + "atomic 0.5.3", + "binascii", + "bytes", + "either", + "figment", + "futures", + "indexmap", + "log", "memchr", + "multer", "num_cpus", - "pear", + "parking_lot", + "pin-project-lite", + "rand", + "ref-cast", "rocket_codegen", "rocket_http", + "serde", + "serde_json", "state", + "tempfile", "time", - "toml", - "version_check 0.9.4", + "tokio", + "tokio-stream", + "tokio-util", + "ubyte", + "version_check", "yansi", ] -[[package]] -name = "rocket-multipart-form-data" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73d29a55443ad045eea2121f6047f25dc2f10eda1adc13ed0f00c12af182831f" -dependencies = [ - "mime 0.3.16", - "multipart", - "rocket", -] - [[package]] name = "rocket_codegen" -version = "0.4.10" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729e687d6d2cf434d174da84fb948f7fef4fac22d20ce94ca61c28b72dbcf9f" +checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" dependencies = [ "devise", "glob", "indexmap", - "quote 0.6.13", + "proc-macro2 1.0.85", + "quote 1.0.36", "rocket_http", - "version_check 0.9.4", - "yansi", + "syn 2.0.66", + "unicode-xid 0.2.4", + "version_check", ] [[package]] -name = "rocket_contrib" -version = "0.4.10" +name = "rocket_db_pools" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b6303dccab46dce6c7ac26c9b9d8d8cde1b19614b027c3f913be6611bff6d9b" +checksum = "c6578b2740ceee3e78bff63fe9299d964b7e68318446cdcb9af3b9cab46e1e9d" dependencies = [ + "deadpool", "diesel", - "log 0.4.16", - "notify", - "r2d2", + "diesel-async", "rocket", - "rocket_contrib_codegen", - "serde", - "serde_json", + "rocket_db_pools_codegen", + "version_check", ] [[package]] -name = "rocket_contrib_codegen" -version = "0.4.10" +name = "rocket_db_pools_codegen" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f2cbcb6c09b3ac0acdf77682ff8c9d1f317361498a773ee50b32be7fddfe2b" +checksum = "842e859f2e87a23efc0f81e25756c0fb43f18726e62daf99da7ea19fbc56cebd" dependencies = [ "devise", - "quote 0.6.13", - "version_check 0.9.4", - "yansi", + "quote 1.0.36", ] [[package]] name = "rocket_http" -version = "0.4.10" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6131e6e6d38a9817f4a494ff5da95971451c2eb56a53915579fc9c80f6ef0117" +checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" dependencies = [ "cookie", - "hyper 0.10.16", + "either", + "futures", + "http 0.2.12", + "hyper", "indexmap", + "log", + "memchr", "pear", - "percent-encoding 1.0.1", + "percent-encoding", + "pin-project-lite", + "ref-cast", + "serde", "smallvec", + "stable-pattern", "state", "time", - "unicode-xid 0.1.0", + "tokio", + "uncased", ] [[package]] name = "rust-ini" -version = "0.15.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3679dd538c876a7b606f3bb951c8a20fc281a0ff7795f59f7cb490e3f979e1" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "ordered-multimap", ] [[package]] name = "rust-s3" -version = "0.26.4" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a04bdd0f5118d06ef0e2daa658a9e5135282bcecfd6c46b11fddf47b1d5d736d" +checksum = "1b2ac5ff6acfbe74226fa701b5ef793aaa054055c13ebb7060ad36942956e027" dependencies = [ - "async-std", + "async-trait", "aws-creds", "aws-region", - "base64 0.13.0", + "base64 0.13.1", + "bytes", "cfg-if 1.0.0", - "chrono", "futures", "hex", - "hmac 0.9.0", - "http", - "log 0.4.16", + "hmac", + "http 0.2.12", + "log", + "maybe-async", "md5", - "percent-encoding 2.1.0", + "minidom", + "percent-encoding", + "quick-xml", "reqwest", "serde", - "serde-xml-rs", "serde_derive", "sha2", - "simpl", + "thiserror", + "time", "tokio", - "url 2.2.2", - "uuid", + "tokio-stream", + "url", ] [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "ryu" -version = "1.0.9" +name = "rustix" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] [[package]] -name = "safemem" -version = "0.3.3" +name = "rustls-pemfile" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] [[package]] -name = "same-file" -version = "1.0.6" +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "rxml" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "a98f186c7a2f3abbffb802984b7f1dfd65dac8be1aafdaabbca4137f53f0dff7" dependencies = [ - "winapi-util", + "bytes", + "rxml_validation", + "smartstring", ] +[[package]] +name = "rxml_validation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "lazy_static", - "winapi 0.3.9", + "windows-sys 0.52.0", ] [[package]] -name = "scheduled-thread-pool" -version = "0.2.5" +name = "scoped-futures" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" +checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" dependencies = [ - "parking_lot", + "cfg-if 1.0.0", + "pin-utils", ] [[package]] -name = "scoped_threadpool" -version = "0.1.9" +name = "scoped-tls" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.6.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -2533,9 +2516,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -2543,34 +2526,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] -[[package]] -name = "serde-xml-rs" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0bf1ba0696ccf0872866277143ff1fd14d22eec235d2b23702f95e6660f7dfa" -dependencies = [ - "log 0.4.16", - "serde", - "thiserror", - "xml-rs", -] - [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] @@ -2585,15 +2556,24 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ - "itoa 1.0.1", + "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2601,86 +2581,143 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.1", + "itoa", "ryu", "serde", ] [[package]] -name = "sha-1" -version = "0.8.2" +name = "sha2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", + "cfg-if 1.0.0", + "cpufeatures", + "digest", ] [[package]] -name = "sha2" -version = "0.9.9" +name = "sharded-slab" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", + "lazy_static", ] [[package]] -name = "simpl" -version = "0.1.0" +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a30f10c911c0355f80f1c2faa8096efc4a58cdf8590b954d5b395efa071c711" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.8.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] -name = "socket2" -version = "0.3.19" +name = "smartstring" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi 0.3.9", + "autocfg", + "static_assertions", + "version_check", ] [[package]] name = "socket2" -version = "0.4.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "winapi 0.3.9", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable-pattern" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" +dependencies = [ + "memchr", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "state" -version = "0.4.2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + +[[package]] +name = "static_assertions" +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 = "3015a7d0a5fd5105c91c3710d42f9ccf0abfb287d62206484dcc67f9569a6483" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -2695,241 +2732,422 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.90" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "unicode-xid 0.2.2", + "proc-macro2 1.0.85", + "quote 1.0.36", + "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", - "unicode-xid 0.2.2", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", + "unicode-xid 0.2.4", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] name = "tempfile" -version = "3.3.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if 1.0.0", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi 0.3.9", + "rustix", + "windows-sys 0.52.0", ] [[package]] name = "termtree" -version = "0.2.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "thread_local" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "cfg-if 1.0.0", + "once_cell", ] [[package]] name = "tiff" -version = "0.6.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ + "flate2", "jpeg-decoder", - "miniz_oxide 0.4.4", "weezl", ] [[package]] name = "time" -version = "0.1.44" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", ] [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "0.2.25" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "iovec", - "lazy_static", - "memchr", + "backtrace", + "bytes", + "libc", "mio", - "pin-project-lite 0.1.12", - "slab", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2", "tokio-macros", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "0.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] -name = "tokio-tls" +name = "tokio-native-tls" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", ] [[package]] -name = "tokio-util" -version = "0.3.1" +name = "tokio-postgres" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" dependencies = [ - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log 0.4.16", - "pin-project-lite 0.1.12", + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand", + "socket2", "tokio", + "tokio-util", + "whoami", ] [[package]] -name = "toml" -version = "0.4.10" +name = "tokio-stream" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ - "serde", + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] -name = "tower-service" -version = "0.3.1" +name = "tokio-util" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] [[package]] -name = "tracing" -version = "0.1.32" +name = "toml" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "cfg-if 1.0.0", - "log 0.4.16", - "pin-project-lite 0.2.8", - "tracing-core", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", ] [[package]] -name = "tracing-core" -version = "0.1.24" +name = "toml" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ - "lazy_static", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.14", ] [[package]] -name = "tracing-futures" -version = "0.2.5" +name = "toml_datetime" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ - "pin-project", - "tracing", + "serde", ] [[package]] -name = "traitobject" -version = "0.1.0" +name = "toml_edit" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] [[package]] -name = "try-lock" -version = "0.2.3" +name = "toml_edit" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.13", +] + +[[package]] +name = "tower-service" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] -name = "twoway" -version = "0.1.8" +name = "tracing" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "memchr", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "typeable" -version = "0.1.2" +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typescript-definitions" version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352629d5465cef9f3ad1b4ae24ca580a2e8f8e8b80ef8a448380d87e9e7d4a4d" +source = "git+https://github.com/onelson/typescript-definitions?branch=no-debug-attrs#f21e330d3a2d3adf9ef7aaae03972e2d80d754ff" dependencies = [ "serde", "typescript-definitions-derive", @@ -2938,8 +3156,7 @@ dependencies = [ [[package]] name = "typescript-definitions-derive" version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c12ccd443e3c246a02411809291a00187ba66e20b941932170f2dd4a68ba1d" +source = "git+https://github.com/onelson/typescript-definitions?branch=no-debug-attrs#f21e330d3a2d3adf9ef7aaae03972e2d80d754ff" dependencies = [ "cfg-if 0.1.10", "failure", @@ -2954,44 +3171,57 @@ dependencies = [ ] [[package]] -name = "ucd-trie" -version = "0.1.3" +name = "ubyte" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" +dependencies = [ + "serde", +] [[package]] -name = "unicase" -version = "1.4.2" +name = "ucd-trie" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -dependencies = [ - "version_check 0.1.5", -] +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] -name = "unicase" -version = "2.6.0" +name = "uncased" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" dependencies = [ - "version_check 0.9.4", + "serde", + "version_check", ] [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -3000,99 +3230,98 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "generic-array 0.14.5", + "crypto-common", "subtle", ] [[package]] name = "url" -version = "1.7.2" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", + "form_urlencoded", + "idna 1.0.0", + "percent-encoding", ] [[package]] -name = "url" -version = "2.2.2" +name = "utf16_iter" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna 0.2.3", - "matches", - "percent-encoding 2.1.0", -] +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "0.8.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ - "getrandom 0.2.6", + "getrandom", ] [[package]] name = "validator" -version = "0.13.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be110dc66fa015b8b1d2c4eae40c495a27fae55f82b9cae3efb8178241ed20eb" +checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" dependencies = [ - "idna 0.2.3", + "idna 0.4.0", "lazy_static", "regex", "serde", "serde_derive", "serde_json", - "url 2.2.2", - "validator_types", + "url", ] [[package]] name = "validator_derive" -version = "0.13.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f14fe757e2894ce4271991901567be07fbc3eac6b24246122214e1d5a16554" +checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af" dependencies = [ "if_chain", "lazy_static", "proc-macro-error", - "proc-macro2 1.0.36", - "quote 1.0.17", + "proc-macro2 1.0.85", + "quote 1.0.36", "regex", - "syn 1.0.90", + "syn 1.0.109", "validator_types", ] [[package]] name = "validator_types" -version = "0.12.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" +checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" +dependencies = [ + "proc-macro2 1.0.85", + "syn 1.0.109", +] [[package]] -name = "value-bag" -version = "1.0.0-alpha.8" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f" -dependencies = [ - "ctor", - "version_check 0.9.4", -] +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vcpkg" @@ -3100,12 +3329,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.4" @@ -3114,102 +3337,80 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vinoteca" -version = "6.2.9" +version = "6.3.0" dependencies = [ "bcrypt", "chrono", "diesel", + "diesel-async", "diesel_migrations", "image", "kamadak-exif", "lazy_static", - "log 0.4.16", + "log", "mockall", "rocket", - "rocket-multipart-form-data", - "rocket_codegen", - "rocket_contrib", + "rocket_db_pools", "rust-s3", "serde", "serde_json", - "time", "typescript-definitions", "uuid", "validator", "validator_derive", ] -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi 0.3.9", - "winapi-util", -] - [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log 0.4.16", "try-lock", ] [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +name = "wasite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.79" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if 1.0.0", - "serde", - "serde_json", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.79" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", - "lazy_static", - "log 0.4.16", - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "log", + "once_cell", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.29" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -3219,38 +3420,51 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.79" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote 1.0.17", + "quote 1.0.36", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.79" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.17", - "syn 1.0.90", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.79" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] [[package]] name = "web-sys" -version = "0.3.56" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -3258,25 +3472,21 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] -name = "wepoll-ffi" -version = "0.1.2" +name = "whoami" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ - "cc", + "redox_syscall 0.4.1", + "wasite", + "web-sys", ] -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -3288,59 +3498,301 @@ dependencies = [ ] [[package]] -name = "winapi-build" -version = "0.1.1" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-i686-pc-windows-gnu" +name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "winapi-util" -version = "0.1.5" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "winapi 0.3.9", + "windows-targets 0.48.5", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] [[package]] -name = "winreg" -version = "0.7.0" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "winapi 0.3.9", + "windows-targets 0.48.5", ] [[package]] -name = "ws2_32-sys" -version = "0.2.1" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "windows-targets 0.52.5", ] [[package]] -name = "xml-rs" -version = "0.8.4" +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if 1.0.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +dependencies = [ + "is-terminal", +] + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "synstructure 0.13.1", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "synstructure 0.13.1", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index e6ab5c9e..803dc9b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "vinoteca" description = "Wine purchase tracker" repository = "https://github.com/threecgreen/vinoteca" -version = "6.2.9" +version = "6.3.0" edition = "2021" authors = ["Carter Green "] readme = "./README.md" @@ -12,45 +12,44 @@ categories = ["web-programming"] [dependencies] # Password encryption -bcrypt = "0.9.0" +bcrypt = "0.15.0" # Datetime functionality -chrono = { version = "0.4.19", features = ["serde"] } +chrono = { version = "0.4.31", features = ["serde"] } # basic ORM -diesel = { version = "1.4.7", features = ["postgres", "chrono"] } -diesel_migrations = "1.4.0" +diesel = { version = "2.1.4", features = ["chrono", "postgres"] } +diesel-async = { version = "0.4.1", features = ["async-connection-wrapper"] } +diesel_migrations = "2.1.0" # Image handling -image = "0.23.14" -kamadak-exif = "0.5.4" +image = "0.24.7" +kamadak-exif = "0.5.5" lazy_static = "1.4.0" -log = "0.4.14" -rocket_codegen = "0.4.10" -rocket-multipart-form-data = "0.9.6" +log = "0.4.20" # AWS S3 access -rust-s3 = "0.26.4" +rust-s3 = { version = "0.33.0", features = ["fail-on-err"] } # (De)serialization -serde = { version = "1.0.126", features = ["derive"] } -serde_json = "1.0.64" -time = "0.1.42" -uuid = { version = "0.8.2", features = ["v4"] } +serde = { version = "1.0.192", features = ["derive"] } +serde_json = "1.0.108" +uuid = { version = "1.5.0", features = ["v4"] } # Input validation -validator = "0.13.0" -validator_derive = "0.13.0" +validator = "0.16.1" +validator_derive = "0.16.0" [dependencies.rocket] -version = "0.4.10" -default_features = false -features = ["private-cookies"] +version = "0.5.1" +default-features = false +features = ["secrets", "json"] -[dependencies.rocket_contrib] -version = "0.4.10" +[dependencies.rocket_db_pools] +version = "0.2.0" default-features = false -features = ["json", "diesel_postgres_pool"] +features = ["diesel_postgres"] # Generate typescript type definitions from rust structs [dependencies.typescript-definitions] -version = "0.1.10" +git = "https://github.com/onelson/typescript-definitions" +branch = "no-debug-attrs" # Enable export in release build features = ["export-typescript"] [dev-dependencies] -mockall = "0.9.1" +mockall = "0.11.4" diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index bf867e0a..00000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly diff --git a/src/bin/vinoteca.rs b/src/bin/vinoteca.rs index 393e5e30..ee9d5f1a 100644 --- a/src/bin/vinoteca.rs +++ b/src/bin/vinoteca.rs @@ -1,14 +1,20 @@ +use rocket::Rocket; use std::env; use std::process; -fn run() { - vinoteca::create_rocket().launch(); -} - -fn main() { +#[rocket::main] +async fn main() { let args: Vec<_> = env::args().collect(); if args.len() == 1 { - run() + match vinoteca::create_rocket().await { + Ok(rocket) => { + let _ = Rocket::launch(rocket).await; + } + Err(e) => { + print_error(&format!("Error launching rocket: {}", e)); + process::exit(2) + } + }; } else { let option = &args[1]; match option.as_str() { diff --git a/src/cached_static.rs b/src/cached_static.rs index 00bf2b1a..3df8ccaf 100644 --- a/src/cached_static.rs +++ b/src/cached_static.rs @@ -1,20 +1,24 @@ +use std::borrow::Cow; +use std::path::{Path, PathBuf}; + use chrono::{DateTime, Utc}; -use rocket::handler::{Handler, Outcome}; -use rocket::http::hyper::header::{CacheControl, CacheDirective, ContentEncoding, Encoding}; -use rocket::http::{uncased::Uncased, uri::Segments, ContentType, Header, Method, Status}; +use rocket::http::{uncased::Uncased, ContentType, Header, Method, Status}; use rocket::response::{self, Responder, Response}; +use rocket::route::Outcome; +use rocket::tokio::fs::{self, File}; +use rocket::tokio::io; +use rocket::{ + http::hyper::header::{CACHE_CONTROL, CONTENT_ENCODING}, + route::Handler, +}; use rocket::{Data, Request, Route}; -use std::borrow::Cow; -use std::fs::{self, File}; -use std::io; -use std::path::{Path, PathBuf}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NotModified(pub R); /// Sets the status code of the response to 304 Not Modified. -impl<'r, R: Responder<'r>> Responder<'r> for NotModified { - fn respond_to(self, _req: &Request) -> Result, Status> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for NotModified { + fn respond_to(self, _req: &Request) -> Result, Status> { Response::build().status(Status::NotModified).ok() } } @@ -24,27 +28,31 @@ impl<'r, R: Responder<'r>> Responder<'r> for NotModified { pub struct CachedFile { path: PathBuf, file: File, + last_modified: DateTime, } impl CachedFile { /// Attempts to open a file in read-only mode. - pub fn open(path: impl Into) -> io::Result { + pub async fn open(path: impl Into) -> io::Result { let path = path.into(); - let file = File::open(&path)?; - Ok(CachedFile { path, file }) - } - - /// Retrieve the underlying `File`. - pub fn file(&self) -> &File { - &self.file + let file = File::open(&path).await?; + let last_modified: DateTime = { + let metadata = fs::metadata(&path).await.unwrap(); + metadata.modified().unwrap().into() + }; + Ok(CachedFile { + path, + file, + last_modified, + }) } } /// Streams the named file to the client. Sets or overrides the Content-Type in /// the response according to the file's extension if the extension is /// recognized. -impl<'r> Responder<'r> for CachedFile { - fn respond_to(self, req: &Request) -> response::Result<'r> { +impl<'r, 'o: 'r> Responder<'r, 'o> for CachedFile { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { let mut response = self.file.respond_to(req)?; if let Some(ext) = self.path.extension() { if ext == "gz" { @@ -59,7 +67,7 @@ impl<'r> Responder<'r> for CachedFile { { // Support for gzipped code, e.g. js or css response.set_header(content_type); - response.set_header(ContentEncoding(vec![Encoding::Gzip])); + response.set_raw_header(CONTENT_ENCODING.to_string(), "gzip"); } else { response.set_header(ContentType::new("application", "gzip")); } @@ -69,21 +77,17 @@ impl<'r> Responder<'r> for CachedFile { response.set_header(content_type); } } - let epoch: DateTime = { - let metadata = fs::metadata(&self.path).unwrap(); - metadata.modified().unwrap().into() - }; response.set_header(Header { name: Uncased::new("Last-Modified"), - value: Cow::from(epoch.to_rfc2822()), + value: Cow::from(self.last_modified.to_rfc2822()), }); // User agent must revalidate. This is especially important for JS bundles - response.set_header(CacheControl(vec![CacheDirective::NoCache])); + response.set_raw_header(CACHE_CONTROL.to_string(), "no-cache"); if req.headers().contains("If-Modified-Since") { if let Some(if_modified_since) = req.headers().get_one("If-Modified-Since") { if let Ok(if_modified_since) = DateTime::parse_from_rfc2822(if_modified_since) { - if epoch <= if_modified_since { + if self.last_modified <= if_modified_since { return NotModified("Not Modified").respond_to(req); } } @@ -93,16 +97,6 @@ impl<'r> Responder<'r> for CachedFile { } } -impl io::Read for CachedFile { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.file.read(buf) - } - - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.file.read_to_end(buf) - } -} - /// Modified version of `rocket_contrib::serve::StaticFiles` that sets last /// modified time and caching #[derive(Debug, Clone)] @@ -138,26 +132,25 @@ impl From for Vec { } } +#[rocket::async_trait] impl Handler for CachedStaticFiles { - fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> Outcome<'r> { + async fn handle<'r>(&self, req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r> { // If this is not the route with segments, handle it only if the user // requested a handling of index files. let current_route = req.route().expect("route while handling"); let is_segments_route = current_route.uri.path().ends_with('>'); if !is_segments_route { - return Outcome::forward(data); - } - + return Outcome::forward(data, Status::Ok); + }; let path = req - .get_segments::>(0) - .and_then(|res| res.ok()) - .and_then(|segments| segments.into_path_buf(false).ok()) - .map(|path| self.root.join(path)); - - match &path { - Some(path) if path.is_dir() => Outcome::forward(data), + .routed_segments(0..) + .to_path_buf(false /* allow dotfiles */) + .ok() + .map(|pb| self.root.join(pb)); + match path { + Some(path) if path.is_dir() => Outcome::forward(data, Status::Ok), Some(path) if path.exists() => { - let gz_path = &&PathBuf::from(&format!("{}.gz", path.to_string_lossy())); + let gz_path = PathBuf::from(&format!("{}.gz", path.to_string_lossy())); let accept_encoding: Option> = req .headers() .get_one("Accept-Encoding") @@ -168,19 +161,19 @@ impl Handler for CachedStaticFiles { .unwrap_or(false) { // TODO: make CachedGzFile struct - Outcome::from(req, CachedFile::open(gz_path).ok()) + Outcome::from(req, CachedFile::open(gz_path).await.ok()) } else { - Outcome::from(req, CachedFile::open(path).ok()) + Outcome::from(req, CachedFile::open(path).await.ok()) } } Some(path) => { warn!( - "Request received for static file that doesn't exist at path '{:?}'", - path + "Request received for static file that doesn't exist at path {:?}. Root: {:?}", + path, self.root ); - Outcome::failure(Status::NotFound) + Outcome::error(Status::NotFound) } - None => Outcome::forward(data), + None => Outcome::forward(data, Status::BadRequest), } } } diff --git a/src/colors.rs b/src/colors.rs index 54bdd88a..e4587531 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -1,15 +1,21 @@ +use diesel::dsl::sql; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; + use crate::error::{RestResult, VinotecaError}; use crate::models::{generic, Color}; use crate::schema::{colors, purchases, wines}; use crate::users::Auth; -use crate::DbConn; - -use diesel::dsl::sql; -use diesel::prelude::*; -use rocket_contrib::json::Json; +use crate::Db; #[get("/colors?&")] -pub fn get(id: Option, name: Option, connection: DbConn) -> RestResult> { +pub async fn get( + id: Option, + name: Option, + mut conn: Connection, +) -> RestResult> { let mut query = colors::table.into_boxed(); if let Some(id) = id { query = query.filter(colors::id.eq(id)); @@ -17,14 +23,16 @@ pub fn get(id: Option, name: Option, connection: DbConn) -> RestRes if let Some(name) = name { query = query.filter(colors::name.eq(name)); } + query - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/colors/top")] -pub fn top(auth: Auth, connection: DbConn) -> RestResult> { +pub async fn top(auth: Auth, mut conn: Connection) -> RestResult> { let limit = 20; top_table!( colors::table @@ -33,6 +41,6 @@ pub fn top(auth: Auth, connection: DbConn) -> RestResult colors::id, colors::name, limit, - connection + conn ) } diff --git a/src/config.rs b/src/config.rs index 73438f86..2fe2e787 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,15 +1,8 @@ -use crate::storage::Storage; +use serde::Deserialize; /// Holds runtime configurations -pub struct Config { - /// Storage object for user images and other files - pub storage: Box, -} - -impl Config { - pub fn new(storage: S) -> Config { - Config { - storage: Box::new(storage), - } - } +#[derive(Debug, Deserialize)] +pub struct AppConfig { + pub aws_access_key: String, + pub aws_secret_key: String, } diff --git a/src/error.rs b/src/error.rs index 2539ab0f..73c225c3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,8 +5,8 @@ use image::ImageError; use rocket::http::{uncased::Uncased, Header, Status}; use rocket::request::Request; use rocket::response::{self, Responder}; -use rocket_contrib::json::Json; -use s3::S3Error; +use rocket::serde::json::Json; +use s3::error::S3Error; use serde::Serialize; use std::borrow::Cow; use std::convert::From; @@ -33,7 +33,7 @@ pub enum VinotecaError { pub type RestResult = Result, VinotecaError>; -impl<'r> Responder<'r> for VinotecaError { +impl<'r> Responder<'r, 'static> for VinotecaError { fn respond_to(self, req: &Request) -> response::Result<'static> { // Return JSON or HTML depending on accept header let mut res = if req @@ -68,7 +68,7 @@ impl From for VinotecaError { fn from(bcrypt_error: BcryptError) -> Self { let msg = "Error hashing password".to_owned(); match bcrypt_error { - BcryptError::InvalidPassword => VinotecaError::Forbidden("Bad password".to_owned()), + BcryptError::InvalidHash(_) => VinotecaError::Forbidden("Bad password".to_owned()), e => { error!("Error performing password hash: {:?}", e); VinotecaError::Internal(msg) @@ -166,7 +166,7 @@ mod tests { use rocket::{ http::{Accept, ContentType}, - local::Client, + local::blocking::Client, }; #[get("/")] @@ -176,7 +176,7 @@ mod tests { fn error_rocket_client() -> Client { let rocket = simple_rocket().mount("/", routes![handle_err]); - let client = Client::new(rocket).unwrap(); + let client = Client::untracked(rocket).unwrap(); client } diff --git a/src/grapes.rs b/src/grapes.rs index 758fef37..25b30bda 100644 --- a/src/grapes.rs +++ b/src/grapes.rs @@ -1,28 +1,28 @@ +use rocket::serde::json::Json; +use rocket_db_pools::diesel::{dsl::count_star, prelude::*}; +use rocket_db_pools::Connection; +use validator::Validate; + use crate::error::{RestResult, VinotecaError}; use crate::models::{generic, Grape, GrapeForm, NewGrape}; use crate::query_utils::IntoFirst; use crate::schema::{grapes, purchases, wine_grapes, wines}; use crate::users::Auth; -use crate::DbConn; - -use diesel::dsl::sql; -use diesel::prelude::*; -use diesel::sql_types::BigInt; -use rocket_contrib::json::Json; -use validator::Validate; +use crate::Db; #[get("/grapes?&")] -pub fn get( +pub async fn get( auth: Auth, id: Option, name: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = grapes::table // Left to include grapes with no wine - .left_join(wine_grapes::table.inner_join(wines::table)) + .left_outer_join(wine_grapes::table.inner_join(wines::table)) .filter(grapes::user_id.eq(auth.id)) .group_by((grapes::id, grapes::name)) + .select((grapes::id, grapes::name, count_star())) .into_boxed(); if let Some(id) = id { query = query.filter(grapes::id.eq(id)); @@ -31,17 +31,17 @@ pub fn get( query = query.filter(grapes::name.eq(name)); } query - .select((grapes::id, grapes::name, sql::("count(wines.id)"))) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/grapes/top?")] -pub fn top( +pub async fn top( auth: Auth, limit: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let limit = limit.unwrap_or(10); top_table!( @@ -51,31 +51,37 @@ pub fn top( grapes::id, grapes::name, limit, - connection + conn ) } #[post("/grapes", format = "json", data = "")] -pub fn post(auth: Auth, grape_form: Json, connection: DbConn) -> RestResult { +pub async fn post( + auth: Auth, + grape_form: Json, + mut conn: Connection, +) -> RestResult { let grape_form = grape_form.into_inner(); grape_form.validate()?; - diesel::insert_into(grapes::table) + let grape_id = diesel::insert_into(grapes::table) .values(NewGrape::from((auth, grape_form))) .returning(grapes::id) - .get_result(&*connection) - .map_err(VinotecaError::from) - .and_then(|grape_id| { - get(auth, Some(grape_id), None, connection)?.into_first("Newly-created grape") - }) + .get_result(&mut **conn) + .await + .map_err(VinotecaError::from)?; + + get(auth, Some(grape_id), None, conn) + .await? + .into_first("Newly-created grape") } #[put("/grapes/", format = "json", data = "")] -pub fn put( +pub async fn put<'r>( auth: Auth, id: i32, grape_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let grape_form = grape_form.into_inner(); grape_form.validate()?; @@ -84,23 +90,29 @@ pub fn put( .filter(grapes::id.eq(id)) .filter(grapes::user_id.eq(auth.id)) .select(grapes::id) - .first::(&*connection)?; + .first::(&mut **conn) + .await?; diesel::update(grapes::table.filter(grapes::id.eq(id))) .set(grapes::name.eq(grape_form.name)) - .execute(&*connection) - .map_err(VinotecaError::from) - .and_then(|_| get(auth, Some(id), None, connection)?.into_first("Edited grape")) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + + get(auth, Some(id), None, conn) + .await? + .into_first("Edited grape") } #[delete("/grapes/")] -pub fn delete(auth: Auth, id: i32, connection: DbConn) -> RestResult<()> { +pub async fn delete(auth: Auth, id: i32, mut conn: Connection) -> RestResult<()> { diesel::delete( grapes::table .filter(grapes::id.eq(id)) .filter(grapes::user_id.eq(auth.id)), ) - .execute(&*connection) + .execute(&mut **conn) + .await .map(|_| Json(())) .map_err(VinotecaError::from) } diff --git a/src/lib.rs b/src/lib.rs index ac32babb..c9dba79c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,10 @@ -#![feature(decl_macro, proc_macro_hygiene)] +#![allow(clippy::extra_unused_lifetimes)] #[macro_use] extern crate diesel; #[macro_use] extern crate lazy_static; #[macro_use] -extern crate log; -#[macro_use] -extern crate rocket_contrib; -#[macro_use] extern crate rocket; #[macro_use] extern crate validator_derive; @@ -49,38 +45,49 @@ pub mod wine_grapes; mod wine_types; pub mod wines; +use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; +use rocket::{fairing::AdHoc, Rocket}; +use rocket_db_pools::{diesel::async_connection_wrapper::AsyncConnectionWrapper, Database}; +use storage::Storage; + use cached_static::CachedStaticFiles; -use query_utils::DbConn; +use query_utils::Db; -use rocket::fairing::AdHoc; -use rocket::Rocket; +pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); -#[macro_use] -extern crate diesel_migrations; -embed_migrations!(); +pub async fn run_db_migrations( + rocket: Rocket, +) -> Result, Rocket> { + let db = Db::fetch(&rocket).unwrap(); + let conn = db.get().await.unwrap(); -pub fn run_db_migrations(rocket: Rocket) -> Result { - let connection = DbConn::get_one(&rocket).expect("database connection"); - match embedded_migrations::run(&*connection) { - Ok(()) => { - info!("Successfully ran database migrations"); - Ok(rocket) - } - Err(e) => { - error!("Failed to run database migrations: {:?}", e); - Err(rocket) - } - } + // let mut async_wrapper = AsyncConnectionWrapper::establish(rocket.figment().extract().unwrap()); + // rocket::tokio::task::spawn_blocking(move || { + // match async_wrapper.run_pending_migrations(MIGRATIONS) { + // Ok(_) => { + // info!("Successfully ran database migrations"); + // Ok(rocket) + // } + // Err(e) => { + // error!("Failed to run database migrations: {e:?}"); + // Err(rocket) + // } + // } + // }) + // .await + // .unwrap() + Ok(rocket) } -pub fn create_rocket() -> rocket::Rocket { - let mut rocket = rocket::ignite(); - - rocket = rocket +pub async fn create_rocket() -> Result, rocket::Error> { + let rocket = rocket::build() // Allow handlers access to the database - .attach(DbConn::fairing()) + .attach(Db::init()) // Run embedded database migrations on startup - .attach(AdHoc::on_attach("Database migrations", run_db_migrations)) + .attach(AdHoc::try_on_ignite( + "Database migrations", + run_db_migrations, + )) .mount("/", static_handlers::get_routes()) .mount( "/rest", @@ -130,7 +137,7 @@ pub fn create_rocket() -> rocket::Rocket { wines::patch, wines::post, wines::put, - wines::delete, + wines::delete_, wines::inventory, wines::search, wines::varieties, @@ -146,30 +153,25 @@ pub fn create_rocket() -> rocket::Rocket { wine_types::top, ], ) + // TODO: register separate handlers for / // These errors should only happen with rest requests so they also return JSON - .register(catchers![ - catchers::unauthorized, - catchers::forbidden, - catchers::not_found - ]); - let static_dir = rocket - .config() - .get_str("static_dir") - .unwrap_or("web/static") - .to_owned(); - let aws_access_key = rocket - .config() - .get_str("aws_access_key") - .expect("AWS access key") - .to_owned(); - let aws_secret_key = rocket - .config() - .get_str("aws_secret_key") - .expect("AWS secret key") - .to_owned(); - let storage = - storage::S3::new(&aws_access_key, &aws_secret_key).expect("AWS S3 bucket connection"); + .register( + "/rest", + catchers![ + catchers::unauthorized, + catchers::forbidden, + catchers::not_found + ], + ); + let app_config: config::AppConfig = rocket.figment().extract().expect("config"); + let storage: Box = Box::new( + storage::S3::new(&app_config.aws_access_key, &app_config.aws_secret_key) + .expect("AWS S3 bucket connection"), + ); + rocket - .manage(config::Config::new(storage)) - .mount("/static", CachedStaticFiles::from(static_dir).rank(1)) + .manage(storage) + .mount("/static", CachedStaticFiles::from("web/static").rank(1)) + .ignite() + .await } diff --git a/src/logs.rs b/src/logs.rs index 9307d26d..4d8eafa4 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -1,6 +1,6 @@ use crate::users::Auth; -use rocket_contrib::json::Json; +use rocket::serde::json::Json; use serde::{Deserialize, Serialize}; use typescript_definitions::TypeScriptify; diff --git a/src/models.rs b/src/models.rs index 31149d03..dd8af465 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,5 +1,5 @@ use crate::schema::*; -use crate::serde::{trim_opt_string, trim_str}; +use crate::serde::{trim_opt_string, trim_string}; use crate::users::Auth; use chrono::{DateTime, NaiveDate, Utc}; @@ -15,12 +15,12 @@ pub struct Color { } #[derive(Deserialize, Insertable, Validate, TypeScriptify, Debug)] -#[table_name = "colors"] +#[diesel(table_name = colors)] #[serde(rename_all = "camelCase")] -pub struct ColorForm<'a> { +pub struct ColorForm { #[validate(length(min = 1))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, } #[derive(Queryable, Clone, Serialize, TypeScriptify, Debug)] @@ -33,21 +33,21 @@ pub struct Grape { #[derive(Deserialize, Validate, TypeScriptify, Debug)] #[serde(rename_all = "camelCase")] -pub struct GrapeForm<'a> { +pub struct GrapeForm { #[validate(length(min = 1))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, } #[derive(Insertable, Debug)] -#[table_name = "grapes"] -pub struct NewGrape<'a> { - pub name: &'a str, +#[diesel(table_name = grapes)] +pub struct NewGrape { + pub name: String, pub user_id: i32, } -impl<'a> From<(Auth, GrapeForm<'a>)> for NewGrape<'a> { - fn from((auth, form): (Auth, GrapeForm<'a>)) -> Self { +impl From<(Auth, GrapeForm)> for NewGrape { + fn from((auth, form): (Auth, GrapeForm)) -> Self { Self { name: form.name, user_id: auth.id, @@ -65,23 +65,23 @@ pub struct Producer { #[derive(Deserialize, Validate, TypeScriptify, Debug)] #[serde(rename_all = "camelCase")] -pub struct ProducerForm<'a> { +pub struct ProducerForm { #[validate(length(min = 1))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, pub region_id: i32, } #[derive(AsChangeset, Insertable, Debug)] -#[table_name = "producers"] -pub struct NewProducer<'a> { - pub name: &'a str, +#[diesel(table_name = producers)] +pub struct NewProducer { + pub name: String, pub region_id: i32, pub user_id: i32, } -impl<'a> From<(Auth, ProducerForm<'a>)> for NewProducer<'a> { - fn from((auth, form): (Auth, ProducerForm<'a>)) -> Self { +impl From<(Auth, ProducerForm)> for NewProducer { + fn from((auth, form): (Auth, ProducerForm)) -> Self { Self { name: form.name, region_id: form.region_id, @@ -106,8 +106,8 @@ pub struct Purchase { } #[derive(AsChangeset, Deserialize, Insertable, Validate, TypeScriptify, Debug)] -#[changeset_options(treat_none_as_null = "true")] -#[table_name = "purchases"] +#[diesel(treat_none_as_null = true)] +#[diesel(table_name = purchases)] #[serde(rename_all = "camelCase")] pub struct PurchaseForm { #[validate(range(min = 0.0))] @@ -133,12 +133,12 @@ pub struct Region { } #[derive(Deserialize, Insertable, Validate, TypeScriptify, Debug)] -#[table_name = "regions"] +#[diesel(table_name = regions)] #[serde(rename_all = "camelCase")] -pub struct RegionForm<'a> { +pub struct RegionForm { #[validate(length(min = 1))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, } #[derive(Queryable, Clone, Serialize, TypeScriptify, Debug)] @@ -150,21 +150,21 @@ pub struct Store { #[derive(Deserialize, Validate, TypeScriptify, Debug)] #[serde(rename_all = "camelCase")] -pub struct StoreForm<'a> { +pub struct StoreForm { #[validate(length(min = 1))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, } #[derive(Insertable, Debug)] -#[table_name = "stores"] -pub struct NewStore<'a> { - pub name: &'a str, +#[diesel(table_name = stores)] +pub struct NewStore { + pub name: String, pub user_id: i32, } -impl<'a> From<(Auth, StoreForm<'a>)> for NewStore<'a> { - fn from((auth, form): (Auth, StoreForm<'a>)) -> Self { +impl From<(Auth, StoreForm)> for NewStore { + fn from((auth, form): (Auth, StoreForm)) -> Self { Self { name: form.name, user_id: auth.id, @@ -184,6 +184,7 @@ pub struct InternalUser { } #[derive(Clone, Queryable, Serialize, TypeScriptify, Debug)] +#[cfg_attr(test, derive(Deserialize))] #[serde(rename_all = "camelCase")] pub struct User { pub email: String, @@ -208,23 +209,24 @@ impl From for User { } #[derive(Deserialize, Validate, TypeScriptify, Debug)] +#[cfg_attr(test, derive(Serialize))] #[serde(rename_all = "camelCase")] -pub struct UserForm<'a> { +pub struct UserForm { #[validate(email)] - #[serde(deserialize_with = "trim_str")] - pub email: &'a str, + #[serde(deserialize_with = "trim_string")] + pub email: String, #[validate(length(min = 2))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, #[validate(length(min = 8))] - pub password: &'a str, + pub password: String, } #[derive(Insertable, Debug)] -#[table_name = "users"] -pub struct NewUser<'a> { - pub email: &'a str, - pub name: &'a str, +#[diesel(table_name = users)] +pub struct NewUser { + pub email: String, + pub name: String, pub image: Option, pub hash: String, } @@ -240,23 +242,23 @@ pub struct VitiArea { #[derive(Deserialize, Validate, TypeScriptify, Debug)] #[serde(rename_all = "camelCase")] -pub struct VitiAreaForm<'a> { +pub struct VitiAreaForm { #[validate(length(min = 1))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, pub region_id: i32, } #[derive(AsChangeset, Insertable, Debug)] -#[table_name = "viti_areas"] -pub struct NewVitiArea<'a> { - pub name: &'a str, +#[diesel(table_name = viti_areas)] +pub struct NewVitiArea { + pub name: String, pub region_id: i32, pub user_id: i32, } -impl<'a> From<(Auth, VitiAreaForm<'a>)> for NewVitiArea<'a> { - fn from((auth, form): (Auth, VitiAreaForm<'a>)) -> Self { +impl From<(Auth, VitiAreaForm)> for NewVitiArea { + fn from((auth, form): (Auth, VitiAreaForm)) -> Self { Self { name: form.name, region_id: form.region_id, @@ -314,11 +316,12 @@ pub struct WineForm { pub name: Option, pub wine_type_id: i32, pub is_in_shopping_list: bool, + pub has_image: bool, } #[derive(AsChangeset, Insertable, Debug)] -#[changeset_options(treat_none_as_null = "true")] -#[table_name = "wines"] +#[diesel(treat_none_as_null = true)] +#[diesel(table_name = wines)] pub struct NewWine { pub description: Option, pub notes: Option, @@ -349,6 +352,7 @@ impl From for WineForm { name: wine.name, wine_type_id: wine.wine_type_id, is_in_shopping_list: wine.is_in_shopping_list, + has_image: wine.image.is_some(), } } } @@ -382,7 +386,7 @@ pub struct WineGrape { } #[derive(Deserialize, Insertable, Validate, TypeScriptify, Debug)] -#[table_name = "wine_grapes"] +#[diesel(table_name = wine_grapes)] #[serde(rename_all = "camelCase")] pub struct WineGrapeForm { pub percent: Option, @@ -399,21 +403,21 @@ pub struct WineType { #[derive(Deserialize, Validate, TypeScriptify, Debug)] #[serde(rename_all = "camelCase")] -pub struct WineTypeForm<'a> { +pub struct WineTypeForm { #[validate(length(min = 1))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, } #[derive(Insertable, Debug)] -#[table_name = "wine_types"] -pub struct NewWineType<'a> { - pub name: &'a str, +#[diesel(table_name = wine_types)] +pub struct NewWineType { + pub name: String, pub user_id: i32, } -impl<'a> From<(Auth, WineTypeForm<'a>)> for NewWineType<'a> { - fn from((auth, form): (Auth, WineTypeForm<'a>)) -> Self { +impl From<(Auth, WineTypeForm)> for NewWineType { + fn from((auth, form): (Auth, WineTypeForm)) -> Self { Self { name: form.name, user_id: auth.id, diff --git a/src/producers.rs b/src/producers.rs index 97cd158e..00455921 100644 --- a/src/producers.rs +++ b/src/producers.rs @@ -1,23 +1,25 @@ +use diesel::dsl::sql; +use diesel::prelude::*; +use diesel_async::{AsyncPgConnection, RunQueryDsl}; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; +use validator::Validate; + use crate::error::{RestResult, VinotecaError}; use crate::models::{generic, NewProducer, Producer, ProducerForm}; use crate::query_utils::IntoFirst; use crate::schema::{producers, purchases, regions, wines}; use crate::users::Auth; -use crate::DbConn; - -use diesel::dsl::sql; -use diesel::prelude::*; -use rocket_contrib::json::Json; -use validator::Validate; +use crate::Db; #[get("/producers?&&&")] -pub fn get( +pub async fn get( auth: Auth, id: Option, name: Option, region_id: Option, region_name: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = producers::table .inner_join(regions::table) @@ -39,22 +41,24 @@ pub fn get( .select((producers::id, producers::name, producers::region_id)) .distinct(); final_query - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/producers/")] -pub fn get_one(auth: Auth, id: i32, connection: DbConn) -> RestResult { - let producer = get(auth, Some(id), None, None, None, connection)?; + +pub async fn get_one(auth: Auth, id: i32, conn: Connection) -> RestResult { + let producer = get(auth, Some(id), None, None, None, conn).await?; producer.into_first(&format!("Producer with id: {}", id)) } #[get("/producers/top?")] -pub fn top( +pub async fn top( auth: Auth, limit: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let limit = limit.unwrap_or(10); top_table!( @@ -64,67 +68,76 @@ pub fn top( producers::id, producers::name, limit, - connection + conn ) } #[post("/producers", format = "json", data = "")] -pub fn post( +pub async fn post( auth: Auth, producer_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let producer_form = producer_form.into_inner(); producer_form.validate()?; + let new_producer = NewProducer::from((auth, producer_form)); - diesel::insert_into(producers::table) - .values(NewProducer::from((auth, producer_form))) + let producer_id = diesel::insert_into(producers::table) + .values(new_producer) .returning(producers::id) - .get_result::(&*connection) - .map_err(VinotecaError::from) - .and_then(|producer_id| { - get(auth, Some(producer_id), None, None, None, connection)? - .into_first("Newly-created producer") - }) + .get_result::(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get(auth, Some(producer_id), None, None, None, conn) + .await? + .into_first("Newly-created producer") } #[put("/producers/", format = "json", data = "")] -pub fn put( +pub async fn put( auth: Auth, id: i32, producer_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let producer_form = producer_form.into_inner(); producer_form.validate()?; - validate_owns_producer(auth, id, &connection)?; + validate_owns_producer(auth, id, &mut **conn).await?; diesel::update(producers::table.filter(producers::id.eq(id))) .set(NewProducer::from((auth, producer_form))) - .execute(&*connection) - .map_err(VinotecaError::from) - .and_then(|_| { - get(auth, Some(id), None, None, None, connection)?.into_first("Edited producer") - }) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + + get(auth, Some(id), None, None, None, conn) + .await? + .into_first("Edited producer") } #[delete("/producers/")] -pub fn delete(auth: Auth, id: i32, connection: DbConn) -> Result<(), VinotecaError> { - validate_owns_producer(auth, id, &connection)?; +pub async fn delete(auth: Auth, id: i32, mut conn: Connection) -> Result<(), VinotecaError> { + validate_owns_producer(auth, id, &mut **conn).await?; diesel::delete(producers::table.filter(producers::id.eq(id))) - .execute(&*connection) + .execute(&mut **conn) + .await .map(|_| ()) .map_err(VinotecaError::from) } -fn validate_owns_producer(auth: Auth, id: i32, connection: &DbConn) -> Result<(), VinotecaError> { +async fn validate_owns_producer( + auth: Auth, + id: i32, + conn: &mut AsyncPgConnection, +) -> Result<(), VinotecaError> { producers::table .filter(producers::id.eq(id)) .filter(producers::user_id.eq(auth.id)) .select(producers::id) - .first::(&**connection)?; + .first::(conn) + .await?; Ok(()) } diff --git a/src/purchases/models.rs b/src/purchases/models.rs index 6ab913b0..da70940d 100644 --- a/src/purchases/models.rs +++ b/src/purchases/models.rs @@ -29,13 +29,13 @@ pub struct RecentPurchase { #[derive(QueryableByName, Serialize, TypeScriptify, Debug)] #[serde(rename_all = "camelCase")] pub struct YearsPurchases { - #[sql_type = "Integer"] + #[diesel(sql_type = Integer)] pub year: i32, - #[sql_type = "BigInt"] + #[diesel(sql_type = BigInt)] pub quantity: i64, - #[sql_type = "Nullable"] + #[diesel(sql_type = Nullable)] pub total_price: Option, - #[sql_type = "Nullable"] + #[diesel(sql_type = Nullable)] pub avg_price: Option, } @@ -54,6 +54,6 @@ pub struct PurchaseCount { #[derive(Serialize, QueryableByName, TypeScriptify, Debug)] #[serde(rename_all = "camelCase")] pub struct MostCommonPurchaseDate { - #[sql_type = "Nullable"] + #[diesel(sql_type = Nullable)] pub most_common_purchase_date: Option, } diff --git a/src/purchases/mutate.rs b/src/purchases/mutate.rs index 837c2448..bf1a8509 100644 --- a/src/purchases/mutate.rs +++ b/src/purchases/mutate.rs @@ -1,76 +1,84 @@ +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; +use validator::Validate; + use super::read::get; use crate::error::{RestResult, VinotecaError}; use crate::models::{Purchase, PurchaseForm}; use crate::query_utils::IntoFirst; use crate::schema::{purchases, stores, wines}; use crate::users::Auth; -use crate::DbConn; - -use diesel::prelude::*; -use rocket_contrib::json::Json; -use validator::Validate; +use crate::Db; #[post("/purchases", format = "json", data = "")] -pub fn post( +pub async fn post( auth: Auth, purchase_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let purchase_form = purchase_form.into_inner(); purchase_form.validate()?; - validate_relations(auth, &purchase_form, &connection)?; - diesel::insert_into(purchases::table) + validate_relations(auth, &purchase_form, &mut conn).await?; + let purchase_id = diesel::insert_into(purchases::table) .values(&purchase_form) .returning(purchases::id) - .get_result(&*connection) - .map_err(|_| VinotecaError::Internal("Creating new purchase".to_owned())) - .and_then(|purchase_id| { - get(auth, Some(purchase_id), None, None, connection)? - .into_first("Newly-created purchase") - }) + .get_result(&mut **conn) + .await + .map_err(|_| VinotecaError::Internal("Creating new purchase".to_owned()))?; + get(auth, Some(purchase_id), None, None, conn) + .await? + .into_first("Newly-created purchase") } #[put("/purchases/", format = "json", data = "")] -pub fn put( +pub async fn put( auth: Auth, id: i32, purchase_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let purchase_form = purchase_form.into_inner(); purchase_form.validate()?; - validate_owns_wine(auth, id, &connection)?; - validate_relations(auth, &purchase_form, &connection)?; + + validate_owns_wine(auth, id, &mut conn).await?; + validate_relations(auth, &purchase_form, &mut conn).await?; diesel::update(purchases::table.filter(purchases::id.eq(id))) .set(purchase_form) - .execute(&*connection) - .map_err(VinotecaError::from) - .and_then(|_| get(auth, Some(id), None, None, connection)?.into_first("Edited purchase")) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get(auth, Some(id), None, None, conn) + .await? + .into_first("Edited purchase") } #[delete("/purchases/")] -pub fn delete(auth: Auth, id: i32, connection: DbConn) -> Result<(), VinotecaError> { - validate_owns_wine(auth, id, &connection)?; +pub async fn delete(auth: Auth, id: i32, mut conn: Connection) -> Result<(), VinotecaError> { + validate_owns_wine(auth, id, &mut conn).await?; diesel::delete(purchases::table.filter(purchases::id.eq(id))) - .execute(&*connection) + .execute(&mut **conn) + .await .map(|_| ()) .map_err(VinotecaError::from) } -fn validate_relations( +async fn validate_relations( auth: Auth, purchase_form: &PurchaseForm, - connection: &DbConn, + conn: &mut Connection, ) -> Result<(), VinotecaError> { // Validate wine is user's wines::table .filter(wines::id.eq(purchase_form.wine_id)) .filter(wines::user_id.eq(auth.id)) .select(wines::id) - .first::(&**connection) + .first::(&mut **conn) + .await .map_err(|e| { warn!("User tried to create purchase with invalid or another user's wine. wine_id: {}, user_id: {}, error: {:?}", purchase_form.wine_id, auth.id, e); // Not forbidden to prevent leaking info to user @@ -82,7 +90,8 @@ fn validate_relations( .filter(stores::id.eq(store_id)) .filter(stores::user_id.eq(auth.id)) .select(stores::id) - .first::(&**connection) + .first::(&mut **conn) + .await .map_err(|e| { warn!("User tried to create purchase with invalid or another user's store. wine_id: {}, user_id: {}, store_id: {}, error: {:?}", purchase_form.wine_id, auth.id, store_id, e); VinotecaError::BadRequest("Store not found for purchase".to_owned()) @@ -92,16 +101,17 @@ fn validate_relations( } /// Validate is user's purchase -fn validate_owns_wine( +async fn validate_owns_wine( auth: Auth, purchase_id: i32, - connection: &DbConn, + conn: &mut Connection, ) -> Result<(), VinotecaError> { purchases::table .inner_join(wines::table) .filter(purchases::id.eq(purchase_id)) .filter(wines::user_id.eq(auth.id)) .select(purchases::id) - .first::(&**connection)?; + .first::(&mut **conn) + .await?; Ok(()) } diff --git a/src/purchases/read.rs b/src/purchases/read.rs index 7a10c5f6..faf7a409 100644 --- a/src/purchases/read.rs +++ b/src/purchases/read.rs @@ -3,21 +3,23 @@ use crate::error::{RestResult, VinotecaError}; use crate::models::Purchase; use crate::schema::{producers, purchases, regions, stores, wine_types, wines}; use crate::users::Auth; -use crate::DbConn; +use crate::Db; +use diesel_async::RunQueryDsl; +use rocket_db_pools::Connection; use diesel::dsl::{sql, sum}; use diesel::prelude::*; use diesel::sql_query; use diesel::sql_types::{Double, Integer}; -use rocket_contrib::json::Json; +use rocket::serde::json::Json; #[get("/purchases?&&")] -pub fn get( +pub async fn get( auth: Auth, id: Option, wine_id: Option, wine_name: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = purchases::table .left_join(stores::table) @@ -45,16 +47,17 @@ pub fn get( purchases::wine_id, purchases::date, )) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/purchases/recent?")] -pub fn recent( +pub async fn recent( auth: Auth, limit: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let limit = limit.unwrap_or(10); purchases::table @@ -85,43 +88,48 @@ pub fn recent( )) .order_by(purchases::date.desc()) .limit(limit as i64) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/purchases/by-year")] -pub fn by_year(auth: Auth, connection: DbConn) -> RestResult> { +pub async fn by_year(auth: Auth, mut conn: Connection) -> RestResult> { sql_query(include_str!("purchases_by_year.sql")) .bind::(auth.id) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/purchases/total-liters")] -pub fn total_liters(auth: Auth, connection: DbConn) -> Json { - let res = purchases::table +pub async fn total_liters(auth: Auth, mut conn: Connection) -> Json { + let total_liters = purchases::table .inner_join(wines::table) .filter(wines::user_id.eq(auth.id)) .select(sum(sql::( "cast(quantity * 0.75 AS DOUBLE PRECISION)", ))) - .first(&*connection) - .unwrap_or(Some(0.0)); - let total_liters = TotalLiters { - total_liters: res.unwrap_or(0.0), - }; - Json(total_liters) + .first(&mut **conn) + .await + .unwrap_or(Some(0.0)) + .unwrap_or(0.0); + Json(TotalLiters { total_liters }) } #[get("/purchases/most-common-purchase-date")] -pub fn most_common_purchase_date(auth: Auth, connection: DbConn) -> Json { +pub async fn most_common_purchase_date( + auth: Auth, + mut conn: Connection, +) -> Json { let count = purchases::table .inner_join(wines::table) .filter(wines::user_id.eq(auth.id)) .count() - .first(&*connection); + .first(&mut **conn) + .await; if Ok(0) == count || count.is_err() { return Json(MostCommonPurchaseDate { most_common_purchase_date: None, @@ -130,7 +138,8 @@ pub fn most_common_purchase_date(auth: Auth, connection: DbConn) -> Json(auth.id) - .load::(&*connection) + .load::(&mut **conn) + .await .unwrap_or_else(|e| { warn!("Error getting most common purchase date: {}", e); vec![MostCommonPurchaseDate { @@ -142,14 +151,14 @@ pub fn most_common_purchase_date(auth: Auth, connection: DbConn) -> Json Json { - let res = purchases::table +pub async fn count(auth: Auth, mut conn: Connection) -> Json { + let count = purchases::table .inner_join(wines::table) .filter(wines::user_id.eq(auth.id)) .select(sum(purchases::quantity)) - .first::>(&*connection); - let total_liters = PurchaseCount { - count: res.unwrap_or(Some(0)).unwrap_or(0), - }; - Json(total_liters) + .first::>(&mut **conn) + .await + .unwrap_or(Some(0)) + .unwrap_or(0); + Json(PurchaseCount { count }) } diff --git a/src/query_utils.rs b/src/query_utils.rs index e4669b50..7df06375 100644 --- a/src/query_utils.rs +++ b/src/query_utils.rs @@ -1,19 +1,23 @@ use crate::error::{RestResult, VinotecaError}; use diesel::sql_types::Text; -use rocket_contrib::databases::diesel::PgConnection; -use rocket_contrib::json::Json; +use rocket::serde::json::Json; +use rocket_db_pools::{diesel::PgPool, Database}; /// Macro for fetching the `$limit` top rows from `$table`. We use a macro /// because of issues with creating generic diesel functions macro_rules! top_table { - ($table: expr, $id: expr, $name: expr, $limit: expr, $connection: expr) => {{ + ($table: expr, $id: expr, $name: expr, $limit: expr, $conn: expr) => {{ + use rocket::serde::json::Json; + use rocket_db_pools::diesel::{ + dsl::sql, + prelude::*, + sql_types::{BigInt, Double, Nullable}, + }; + use crate::error::VinotecaError; use crate::models::generic; - use diesel::sql_types::{BigInt, Double, Nullable}; - use rocket_contrib::json::Json; - $table .group_by(($id, $name)) .select(( @@ -25,7 +29,8 @@ macro_rules! top_table { )) .order_by(sql::("sum(purchases.quantity) DESC")) .limit($limit as i64) - .load::(&*$connection) + .load::(&mut **$conn) + .await .map(Json) .map_err(VinotecaError::from) }}; @@ -34,8 +39,9 @@ macro_rules! top_table { sql_function!(fn lower(x: Text) -> Text); /// Database connection from connection pool. +#[derive(Database)] #[database("vinoteca")] -pub struct DbConn(PgConnection); +pub struct Db(pub PgPool); /// Used for reusing the `GET` request logic in `POST` and `PUT` methods to /// retrieved the created or modified entity. `GET` methods usually return a diff --git a/src/regions.rs b/src/regions.rs index 4ccd4a42..089f003c 100644 --- a/src/regions.rs +++ b/src/regions.rs @@ -1,21 +1,24 @@ +use diesel::dsl::sql; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; + use crate::error::{RestResult, VinotecaError}; use crate::models::{generic, Region}; use crate::schema::{producers, purchases, regions, wines}; use crate::users::Auth; -use crate::DbConn; - -use diesel::dsl::sql; -use diesel::prelude::*; -use rocket_contrib::json::Json; +use crate::Db; #[get("/regions?&&")] -pub fn get( +pub async fn get( id: Option, name: Option, producer_name: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { - // Still want to include regions that don't have a producer associated with them + // Left join because still want to include regions that don't have a producer associated + // with them let mut query = regions::table.left_join(producers::table).into_boxed(); if let Some(id) = id { query = query.filter(regions::id.eq(id)); @@ -29,16 +32,17 @@ pub fn get( query .select((regions::id, regions::name)) .distinct() - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/regions/top?")] -pub fn top( +pub async fn top( auth: Auth, limit: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let limit = limit.unwrap_or(10); top_table!( @@ -49,6 +53,6 @@ pub fn top( regions::id, regions::name, limit, - connection + conn ) } diff --git a/src/schema.rs b/src/schema.rs index 026e7732..4516c5f1 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,11 +1,11 @@ -table! { +diesel::table! { colors (id) { id -> Int4, name -> Text, } } -table! { +diesel::table! { grapes (id) { id -> Int4, name -> Text, @@ -13,7 +13,7 @@ table! { } } -table! { +diesel::table! { producers (id) { id -> Int4, name -> Text, @@ -22,7 +22,7 @@ table! { } } -table! { +diesel::table! { purchases (id) { id -> Int4, price -> Nullable, @@ -35,14 +35,14 @@ table! { } } -table! { +diesel::table! { regions (id) { id -> Int4, name -> Text, } } -table! { +diesel::table! { stores (id) { id -> Int4, name -> Text, @@ -50,7 +50,7 @@ table! { } } -table! { +diesel::table! { users (id) { id -> Int4, email -> Text, @@ -62,7 +62,7 @@ table! { } } -table! { +diesel::table! { viti_areas (id) { id -> Int4, name -> Text, @@ -71,7 +71,7 @@ table! { } } -table! { +diesel::table! { wine_grapes (wine_id, grape_id) { wine_id -> Int4, grape_id -> Int4, @@ -79,7 +79,7 @@ table! { } } -table! { +diesel::table! { wines (id) { id -> Int4, name -> Nullable, @@ -98,7 +98,7 @@ table! { } } -table! { +diesel::table! { wine_types (id) { id -> Int4, name -> Text, @@ -106,7 +106,7 @@ table! { } } -table! { +diesel::table! { recent_purchases(wine_id) { wine_id -> Int4, price -> Nullable, @@ -118,26 +118,26 @@ table! { } } -joinable!(grapes -> users (user_id)); -joinable!(producers -> regions (region_id)); -joinable!(producers -> users (user_id)); -joinable!(purchases -> stores (store_id)); -joinable!(purchases -> wines (wine_id)); -joinable!(recent_purchases -> stores (store_id)); -joinable!(recent_purchases -> wines (wine_id)); -joinable!(stores -> users (user_id)); -joinable!(viti_areas -> regions (region_id)); -joinable!(viti_areas -> users (user_id)); -joinable!(wine_grapes -> grapes (grape_id)); -joinable!(wine_grapes -> wines (wine_id)); -joinable!(wine_types -> users (user_id)); -joinable!(wines -> colors (color_id)); -joinable!(wines -> producers (producer_id)); -joinable!(wines -> users (user_id)); -joinable!(wines -> viti_areas (viti_area_id)); -joinable!(wines -> wine_types (wine_type_id)); +diesel::joinable!(grapes -> users (user_id)); +diesel::joinable!(producers -> regions (region_id)); +diesel::joinable!(producers -> users (user_id)); +diesel::joinable!(purchases -> stores (store_id)); +diesel::joinable!(purchases -> wines (wine_id)); +diesel::joinable!(recent_purchases -> stores (store_id)); +diesel::joinable!(recent_purchases -> wines (wine_id)); +diesel::joinable!(stores -> users (user_id)); +diesel::joinable!(viti_areas -> regions (region_id)); +diesel::joinable!(viti_areas -> users (user_id)); +diesel::joinable!(wine_grapes -> grapes (grape_id)); +diesel::joinable!(wine_grapes -> wines (wine_id)); +diesel::joinable!(wine_types -> users (user_id)); +diesel::joinable!(wines -> colors (color_id)); +diesel::joinable!(wines -> producers (producer_id)); +diesel::joinable!(wines -> users (user_id)); +diesel::joinable!(wines -> viti_areas (viti_area_id)); +diesel::joinable!(wines -> wine_types (wine_type_id)); -allow_tables_to_appear_in_same_query!( +diesel::allow_tables_to_appear_in_same_query!( colors, grapes, producers, diff --git a/src/serde.rs b/src/serde.rs index dd31e5a2..fe4d941b 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,6 +1,7 @@ /// Custom deserializers to trim leading and trailing whitespace from strings. use serde::{Deserialize, Deserializer}; +#[allow(dead_code)] pub fn trim_str<'de, D>(deserializer: D) -> Result<&'de str, D::Error> where D: Deserializer<'de>, @@ -42,7 +43,7 @@ mod test { #[test] fn trim_str_untouched() { let test_str = "quick brown fox"; - let deserializer = BorrowedStrDeserializer::::new(test_str); + let deserializer = BorrowedStrDeserializer::::new(&test_str); assert_eq!(trim_str(deserializer), Ok(test_str)); } @@ -83,10 +84,8 @@ mod test { } #[test] - #[ignore] fn trim_opt_only_spaces_to_none() { - // let deserializer = serde_json::de::Deserializer::from_str(r#"" ""#); - // let deserializer = Some(" ".to_owned()).into_deserializer(); - // assert_eq!(trim_opt_string(deserializer), Ok(None)); + let deserializer = serde_json::json!(" ").into_deserializer(); + assert!(matches!(trim_opt_string(deserializer), Ok(None))); } } diff --git a/src/static_handlers.rs b/src/static_handlers.rs index 211a93e8..4abd5f9d 100644 --- a/src/static_handlers.rs +++ b/src/static_handlers.rs @@ -1,6 +1,6 @@ use crate::users::Auth; -use rocket::response::content::{Html, Plain, Xml}; +use rocket::response::content::{RawHtml, RawText, RawXml}; pub fn get_routes() -> Vec { routes![ @@ -30,88 +30,88 @@ pub fn get_routes() -> Vec { // Keep in sync with Router.tsx #[get("/")] -pub fn home() -> Html<&'static str> { - Html(include_str!("static/index.html")) +pub fn home() -> RawHtml<&'static str> { + RawHtml(include_str!("static/index.html")) } #[get("/about")] -pub fn about() -> Html<&'static str> { +pub fn about() -> RawHtml<&'static str> { home() } #[get("/about/changelog")] -pub fn changelog() -> Html<&'static str> { +pub fn changelog() -> RawHtml<&'static str> { home() } #[get("/login")] -pub fn login() -> Html<&'static str> { +pub fn login() -> RawHtml<&'static str> { home() } #[get("/register")] -pub fn register() -> Html<&'static str> { +pub fn register() -> RawHtml<&'static str> { home() } #[get("/dashboards")] -pub fn dashboards(_auth: Auth) -> Html<&'static str> { +pub fn dashboards(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/grapes")] -pub fn grapes(_auth: Auth) -> Html<&'static str> { +pub fn grapes(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/producers")] -pub fn producers(_auth: Auth) -> Html<&'static str> { +pub fn producers(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/wines")] -pub fn wines(_auth: Auth) -> Html<&'static str> { +pub fn wines(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/wines/<_id>")] -pub fn wine_profile(_auth: Auth, _id: i32) -> Html<&'static str> { +pub fn wine_profile(_auth: Auth, _id: i32) -> RawHtml<&'static str> { home() } #[get("/wines/inventory")] -pub fn inventory(_auth: Auth) -> Html<&'static str> { +pub fn inventory(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/wines/new")] -pub fn new_wine(_auth: Auth) -> Html<&'static str> { +pub fn new_wine(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/wines/search")] -pub fn search_wines(_auth: Auth) -> Html<&'static str> { +pub fn search_wines(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/wines/shopping-list")] -pub fn shopping_list(_auth: Auth) -> Html<&'static str> { +pub fn shopping_list(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/producers/<_id>")] -pub fn producer_profile(_auth: Auth, _id: i32) -> Html<&'static str> { +pub fn producer_profile(_auth: Auth, _id: i32) -> RawHtml<&'static str> { home() } #[get("/regions/<_id>")] -pub fn region_profile(_auth: Auth, _id: i32) -> Html<&'static str> { +pub fn region_profile(_auth: Auth, _id: i32) -> RawHtml<&'static str> { home() } #[get("/profile")] -pub fn user_profile(_auth: Auth) -> Html<&'static str> { +pub fn user_profile(_auth: Auth) -> RawHtml<&'static str> { home() } #[get("/viti-areas/<_id>")] -pub fn viti_area_profile(_auth: Auth, _id: i32) -> Html<&'static str> { +pub fn viti_area_profile(_auth: Auth, _id: i32) -> RawHtml<&'static str> { home() } #[get("/wine-types/<_id>")] -pub fn wine_type_profile(_auth: Auth, _id: i32) -> Html<&'static str> { +pub fn wine_type_profile(_auth: Auth, _id: i32) -> RawHtml<&'static str> { home() } #[get("/robots.txt")] -pub fn robots() -> Plain<&'static str> { - Plain(include_str!("static/robots.txt")) +pub fn robots() -> RawText<&'static str> { + RawText(include_str!("static/robots.txt")) } #[get("/sitemap.xml")] -pub fn sitemap() -> Xml<&'static str> { - Xml(include_str!("static/sitemap.xml")) +pub fn sitemap() -> RawXml<&'static str> { + RawXml(include_str!("static/sitemap.xml")) } diff --git a/src/storage.rs b/src/storage.rs index 3a77520e..c717b23b 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -7,21 +7,14 @@ use uuid::Uuid; #[cfg(test)] use mockall::{automock, predicate::*}; #[cfg_attr(test, automock)] +#[rocket::async_trait] pub trait Storage: Send + Sync + 'static { - /// Store object `content` in `dir` at a generated path. Returns the path where - /// `content` is stored. - fn create_object( - &self, - dir: &str, - content: &[u8], - content_type: &str, - ) -> Result; /// read object at `path` - fn get_object(&self, path: &str) -> Result, VinotecaError>; - /// update object at `path` - fn update_object(&self, path: &str, content: &[u8]) -> Result<(), VinotecaError>; - /// Delete object at `path`. - fn delete_object(&self, path: &str) -> Result<(), VinotecaError>; + async fn get_object(&self, path: &str) -> Result, VinotecaError>; + /// Get a pre-signed URL for creating an object + async fn presign_create_object(&self, dir: &str) -> Result; + /// Get a pre-signed URL for updating an object + async fn presign_update_object(&self, path: &str) -> Result; } /// Stores files on AWS S3 bucket. @@ -30,73 +23,94 @@ pub struct S3 { bucket: S3Bucket, } +#[derive(Debug)] +pub struct PresignedCreate { + /// path of the pre-signed object + path: String, + /// URL to update the object + url: String, +} + const BUCKET_NAME: &str = "vinoteca"; +const EXPIRY_SECONDS: u32 = 60 * 10; +#[rocket::async_trait] impl Storage for S3 { - fn create_object( - &self, - dir: &str, - content: &[u8], - content_type: &str, - ) -> Result { - let path = Uuid::new_v4().to_string(); - let (data, code) = self.bucket.put_object_with_content_type_blocking( - format!("{}/{}", dir, path), - content, - content_type, - )?; - if code > 304 { - warn!( - "Error saving image. Code: {}, AWSResponseData: {:?}", - code, - String::from_utf8(data) - ); - Err(VinotecaError::Internal("Error saving image".to_owned())) - } else { - Ok(path) - } - } + // async fn create_object( + // &self, + // dir: &str, + // content: &[u8], + // content_type: &str, + // ) -> Result { + // let (data, code) = self + // .bucket + // .put_object_with_content_type(self.path(dir), content, content_type) + // .await?; + // // TODO: may be able to delete code checks with fail on err feature enabled + // if code > 304 { + // warn!( + // "Error saving image. Code: {}, AWSResponseData: {:?}", + // code, + // String::from_utf8(data) + // ); + // Err(VinotecaError::Internal("Error saving image".to_owned())) + // } else { + // Ok(path) + // } + // } - fn get_object(&self, path: &str) -> Result, VinotecaError> { - let (data, code) = self.bucket.get_object_blocking(path)?; - if code > 304 { + async fn get_object(&self, path: &str) -> Result, VinotecaError> { + let resp = self.bucket.get_object(path).await?; + if resp.status_code() > 304 { warn!( "Failed to get file from S3. Code: {}, AWSResponseData: {:?}", - code, - String::from_utf8(data) + resp.status_code(), + resp.as_str() ); Err(VinotecaError::Internal("Error getting file".to_owned())) } else { - Ok(data) + Ok(resp.to_vec()) } } - fn update_object(&self, path: &str, content: &[u8]) -> Result<(), VinotecaError> { - let (data, code) = self.bucket.put_object_blocking(path, content)?; - if code > 304 { - warn!( - "Error saving image. Code: {}, AWSResponseData: {:?}", - code, - String::from_utf8(data) - ); - Err(VinotecaError::Internal("Error saving image".to_owned())) - } else { - Ok(()) - } + // async fn update_object(&self, path: &str, content: &[u8]) -> Result<(), VinotecaError> { + // let (data, code) = self.bucket.put_object(path, content).await?; + // if code > 304 { + // warn!( + // "Error saving image. Code: {}, AWSResponseData: {:?}", + // code, + // String::from_utf8(data) + // ); + // Err(VinotecaError::Internal("Error saving image".to_owned())) + // } else { + // Ok(()) + // } + // } + + // async fn delete_object(&self, path: &str) -> Result<(), VinotecaError> { + // let (data, code) = self.bucket.delete_object(path).await?; + // if code > 304 { + // warn!( + // "Error deleting existing file. Code: {}, AWSResponseData: {:?}", + // code, + // String::from_utf8(data) + // ); + // Err(VinotecaError::Internal("Error deleting file".to_owned())) + // } else { + // Ok(()) + // } + // } + + async fn presign_create_object(&self, dir: &str) -> Result { + let path = Uuid::new_v4().to_string(); + let url = self + .bucket + .presign_put(format!("{dir}/{path}"), EXPIRY_SECONDS, None)?; + Ok(PresignedCreate { path, url }) } - fn delete_object(&self, path: &str) -> Result<(), VinotecaError> { - let (data, code) = self.bucket.delete_object_blocking(path)?; - if code > 304 { - warn!( - "Error deleting existing file. Code: {}, AWSResponseData: {:?}", - code, - String::from_utf8(data) - ); - Err(VinotecaError::Internal("Error deleting file".to_owned())) - } else { - Ok(()) - } + async fn presign_update_object(&self, path: &str) -> Result { + Ok(self.bucket.presign_put(path, EXPIRY_SECONDS, None)?) } } diff --git a/src/stores.rs b/src/stores.rs index fdcfddd7..764947ea 100644 --- a/src/stores.rs +++ b/src/stores.rs @@ -1,20 +1,22 @@ +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; +use validator::Validate; + use crate::error::{RestResult, VinotecaError}; use crate::models::{NewStore, Store, StoreForm}; use crate::query_utils::IntoFirst; use crate::schema::stores; use crate::users::Auth; -use crate::DbConn; - -use diesel::prelude::*; -use rocket_contrib::json::Json; -use validator::Validate; +use crate::Db; #[get("/stores?&")] -pub fn get( +pub async fn get( auth: Auth, id: Option, name: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = stores::table .filter(stores::user_id.eq(auth.id)) @@ -27,22 +29,28 @@ pub fn get( } query .select((stores::id, stores::name)) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[post("/stores", format = "json", data = "")] -pub fn post(auth: Auth, store_form: Json, connection: DbConn) -> RestResult { +pub async fn post( + auth: Auth, + store_form: Json, + mut conn: Connection, +) -> RestResult { let store_form = store_form.into_inner(); store_form.validate()?; - diesel::insert_into(stores::table) + let store_id = diesel::insert_into(stores::table) .values(NewStore::from((auth, store_form))) .returning(stores::id) - .get_result(&*connection) - .map_err(VinotecaError::from) - .and_then(|store_id| { - get(auth, Some(store_id), None, connection)?.into_first("Newly-created store") - }) + .get_result(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get(auth, Some(store_id), None, conn) + .await? + .into_first("Newly-created store") } diff --git a/src/testing.rs b/src/testing.rs index 26ce38d5..9bb9a372 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -1,15 +1,13 @@ use crate::schema::{grapes, producers, users, wine_types, wines}; /// Integration tests -use crate::{run_db_migrations, DbConn}; +use crate::{run_db_migrations, Db}; use diesel::prelude::*; use diesel::sql_query; -use rocket::config::{Config, Environment, Value}; -use rocket::fairing::AdHoc; +use rocket::fairing::{AdHoc, Fairing}; +use rocket::tokio::sync::Mutex; use rocket::Rocket; -use std::collections::HashMap; use std::env; -use std::sync::Mutex; // Use a lock to synchronize between tests so database operations from one // test don't interfere with another @@ -17,26 +15,22 @@ lazy_static! { pub static ref DB_LOCK: Mutex<()> = Mutex::new(()); } -macro_rules! db_test { - (|$rocket: ident, $conn: ident| $test_block: expr) => {{ - use crate::testing::{create_test_rocket, DB_LOCK}; - - let _lock = DB_LOCK.lock(); - let $rocket = create_test_rocket(); - let $conn = DbConn::get_one(&$rocket).expect("database connection"); +/// password for a test user +pub const PW: &str = "testing123"; - // Execute test code - $test_block - // `_lock` is dropped - }}; +lazy_static! { + pub static ref PW_HASH: String = bcrypt::hash(PW, bcrypt::DEFAULT_COST).unwrap(); } -macro_rules! rocket_test { - (|$rocket: ident| $test_block: expr) => {{ +macro_rules! db_test { + (|$rocket: ident, $conn: ident| $test_block: expr) => {{ use crate::testing::{create_test_rocket, DB_LOCK}; - let _lock = DB_LOCK.lock(); - let $rocket = create_test_rocket(); + let _lock = DB_LOCK.lock().await; + let $rocket = create_test_rocket().await; + let $conn = Db::get_one(&$rocket) + .await + .expect("database connection"); // Execute test code $test_block @@ -45,127 +39,134 @@ macro_rules! rocket_test { } /// Doesn't include database access -pub fn simple_rocket() -> Rocket { - let config = Config::build(Environment::Development) - .workers(1) - .finalize() - .unwrap(); +pub fn simple_rocket() -> Rocket { + let mut config = rocket::Config::default(); + config.workers = 1; rocket::custom(config) } /// `MediaDir` -pub fn create_test_rocket() -> Rocket { - test_rocket_config() - .attach(DbConn::fairing()) - .attach(AdHoc::on_attach("Setup test db", setup_test_db)) +pub async fn create_test_rocket() -> Rocket { + let rocket = test_rocket_config(); + // Hack to call these fairings before calling `ignite` on `rocket`. This way we can mount + // individual routes in tests and have more flexibility in general + let rocket = Db::fairing().on_ignite(rocket).await.unwrap(); + AdHoc::try_on_ignite("Setup test db", setup_test_db) + .on_ignite(rocket) + .await + .expect("Successful migration") } -fn test_rocket_config() -> Rocket { - let mut database_config = HashMap::new(); - let mut databases = HashMap::new(); - let rocket_test_db = env::var("ROCKET_TEST_DB").expect("Test database connection string"); +fn test_rocket_config() -> Rocket { + use rocket::figment::{ + self, + value::{Map, Value}, + }; + + let test_db = env::var("ROCKET_TEST_DB").expect("Test database connection string"); // Testing db should have test in the name - assert!(rocket_test_db.contains("test")); - - database_config.insert("url", Value::from(rocket_test_db)); - databases.insert("vinoteca", Value::from(database_config)); - let config = Config::build(Environment::Development) - .workers(1) - .extra("databases", databases) - .finalize() - .unwrap(); + assert!(test_db.contains("test")); + + let db_config: Map<_, Value> = figment::util::map! { + "url" => test_db.into(), + "pool_size" => 2.into(), + }; + + let config = rocket::Config::figment().merge(( + "databases", + rocket::figment::util::map! { "vinoteca" => db_config }, + )); rocket::custom(config) } -fn setup_test_db(rocket: Rocket) -> Result { - let rocket = run_db_migrations(rocket); - if let Ok(rocket) = rocket { - // TODO: test with multiple users - let connection = DbConn::get_one(&rocket).expect("database connection"); - - // TODO: timing logs - let user_id = 1; - // Truncate most tables +async fn setup_test_db( + rocket: Rocket, +) -> Result, Rocket> { + let rocket = run_db_migrations(rocket).await?; + // TODO: test with multiple users + let conn = Db::get_one(&rocket).await.expect("database connection"); + + let user_id = 1; + let mock_grapes = vec![ + "Cabernet Sauvignon", + "Tempranillo", + "Merlot", + "Pinot Noir", + "Riesling", + "Pinot Grigio", + "Garnacha", + ]; + let mock_wine_types = vec![ + "Sauvignon Blanc", + "Pinot Noir", + "Cabernet Sauvignon", + "Chardonnary", + "Cava", + ]; + // Truncate most tables + conn.run(move |c| { sql_query("TRUNCATE TABLE wines, purchases, grapes, wine_grapes, producers, viti_areas, users, wine_types CASCADE;") - .execute(&*connection).unwrap(); - diesel::insert_into(users::table) + .execute(c).unwrap(); + diesel::insert_into(users::table) + .values(( + users::id.eq(user_id), + users::email.eq("test@gmail.com"), + users::name.eq("John Doe"), + users::hash.eq(&*PW_HASH), + )) + .execute(c) + .unwrap(); + + for (i, grape) in mock_grapes.iter().enumerate() { + diesel::insert_into(grapes::table) .values(( - users::id.eq(user_id), - users::email.eq("test@gmail.com"), - users::name.eq("John Doe"), - users::hash.eq("browns"), + grapes::id.eq(i as i32 + 1), + grapes::name.eq(grape), + grapes::user_id.eq(user_id), )) - .execute(&*connection) + .execute(c) .unwrap(); - - let mock_grapes = vec![ - "Cabernet Sauvignon", - "Tempranillo", - "Merlot", - "Pinot Noir", - "Riesling", - "Pinot Grigio", - "Garnacha", - ]; - for (i, grape) in mock_grapes.iter().enumerate() { - diesel::insert_into(grapes::table) - .values(( - grapes::id.eq(i as i32 + 1), - grapes::name.eq(grape), - grapes::user_id.eq(user_id), - )) - .execute(&*connection) - .unwrap(); - } - - for (i, producer) in vec!["Martineli", "Le Grand Noir", "Rodney Strong"] - .iter() - .enumerate() - { - diesel::insert_into(producers::table) - .values(( - producers::id.eq(i as i32), - producers::name.eq(producer), - producers::region_id.eq(if i % 2 == 0 { 1 } else { 2 }), - producers::user_id.eq(user_id), - )) - .execute(&*connection) - .unwrap(); - } - - let mock_wine_types = vec![ - "Sauvignon Blanc", - "Pinot Noir", - "Cabernet Sauvignon", - "Chardonnary", - "Cava", - ]; - for (i, wine_type) in mock_wine_types.iter().enumerate() { - diesel::insert_into(wine_types::table) - .values(( - wine_types::id.eq(i as i32 + 1), - wine_types::name.eq(wine_type), - wine_types::user_id.eq(user_id), - )) - .execute(&*connection) - .unwrap(); - } - - diesel::insert_into(wines::table) + } + for (i, producer) in vec!["Martineli", "Le Grand Noir", "Rodney Strong"] + .iter() + .enumerate() + { + diesel::insert_into(producers::table) .values(( - wines::id.eq(1), - wines::wine_type_id.eq(1), - wines::producer_id.eq(1), - wines::color_id.eq(2), - wines::inventory.eq(0), - wines::user_id.eq(user_id), - wines::image.eq(Some("unique_id")), + producers::id.eq(i as i32), + producers::name.eq(producer), + producers::region_id.eq(if i % 2 == 0 { 1 } else { 2 }), + producers::user_id.eq(user_id), )) - .execute(&*connection) + .execute(c) .unwrap(); + } - Ok(rocket) - } else { - panic!("Failed to create test database") + for (i, wine_type) in mock_wine_types.iter().enumerate() { + diesel::insert_into(wine_types::table) + .values(( + wine_types::id.eq(i as i32 + 1), + wine_types::name.eq(wine_type), + wine_types::user_id.eq(user_id), + )) + .execute(c) + .unwrap(); } + + diesel::insert_into(wines::table) + .values(( + wines::id.eq(1), + wines::wine_type_id.eq(1), + wines::producer_id.eq(1), + wines::color_id.eq(2), + wines::inventory.eq(0), + wines::user_id.eq(user_id), + wines::image.eq(Some("unique_id")), + )) + .execute(c) + .unwrap(); + + }).await; + + Ok(rocket) } diff --git a/src/users/auth.rs b/src/users/auth.rs index 144c8bc7..f5a7740f 100644 --- a/src/users/auth.rs +++ b/src/users/auth.rs @@ -1,14 +1,16 @@ -use crate::error::VinotecaError; -use crate::schema::users; -use crate::DbConn; - use diesel::prelude::*; +use diesel_async::RunQueryDsl; use rocket::http::Status; +use rocket::outcome::Outcome; use rocket::request::{self, FromRequest, Request}; -use rocket::Outcome; -use rocket_contrib::json::Json; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; use serde::{Deserialize, Serialize}; +use crate::error::VinotecaError; +use crate::schema::users; +use crate::Db; + // If more fields are added, remove `Copy` #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub struct Auth { @@ -18,47 +20,50 @@ pub struct Auth { pub const COOKIE_NAME: &str = "vinoteca-auth"; -impl<'a, 'r> FromRequest<'a, 'r> for Auth { +#[rocket::async_trait] +impl<'r> FromRequest<'r> for Auth { type Error = Json; - fn from_request(request: &'a Request<'r>) -> request::Outcome { - let connection = request.guard::().map_failure(|e| { - error!("Failed to acquire database connection: {:?}", e); - ( - Status::InternalServerError, - Json(VinotecaError::Internal( - "Database connection failed".to_owned(), - )), - ) - })?; - let cookie = request.cookies().get_private(COOKIE_NAME); - match cookie { - Some(cookie) => { - let auth = cookie - .value() - .parse() - .ok() - .and_then(|id: i32| { - users::table - .filter(users::id.eq(id)) - .select(users::id) - .first(&*connection) - .ok() - }) - .map(|id| Auth { id }); - if let Some(auth) = auth { - Outcome::Success(auth) - } else { - Outcome::Failure(( - Status::Forbidden, - Json(VinotecaError::Forbidden("Bad email or password".to_owned())), - )) - } + async fn from_request(request: &'r Request<'_>) -> request::Outcome { + let mut conn = + rocket::outcome::try_outcome!(request.guard::>().await.map_error(|e| { + error!("Failed to acquire database connection: {e:?}"); + ( + Status::InternalServerError, + Json(VinotecaError::Internal( + "Database connection failed".to_owned(), + )), + ) + })); + // Extract user id from cookie + let user_id: Option = request + .cookies() + .get_private(COOKIE_NAME) + .and_then(|cookie| cookie.value().parse().ok()); + let user_id = match user_id { + Some(user_id) => user_id, + None => { + return Outcome::Error(( + Status::Unauthorized, + Json(VinotecaError::Unauthorized("Login required".to_owned())), + )); } - None => Outcome::Failure(( - Status::Unauthorized, - Json(VinotecaError::Unauthorized("Login required".to_owned())), - )), + }; + // Verify user id exists + let id = users::table + .filter(users::id.eq(user_id)) + .select(users::id) + .first(&mut **conn) + .await + .ok(); + + if let Some(id) = id { + Outcome::Success(Auth { id }) + } else { + Outcome::Error(( + Status::Forbidden, + Json(VinotecaError::Forbidden("Bad email or password".to_owned())), + )) } } } @@ -67,48 +72,47 @@ impl<'a, 'r> FromRequest<'a, 'r> for Auth { mod test { use super::*; - use rocket::http::Cookie; - use rocket::local::Client; + use rocket::{http::Cookie, local::asynchronous::Client}; #[get("/")] fn handle_auth(auth: Auth) -> Json { Json(auth.id) } - #[test] - fn missing_cookie() { - rocket_test!(|rocket| { + #[rocket::async_test] + async fn missing_cookie() { + db_test!(|rocket, _conn| { let rocket = rocket.mount("/", routes![handle_auth]); - let client = Client::new(rocket).expect("rocket client"); + let client = Client::tracked(rocket).await.expect("rocket client"); let req = client.get("/"); - let response = req.dispatch(); + let response = req.dispatch().await; assert_eq!(response.status(), Status::Unauthorized); }) } - #[test] - fn missing_user() { - rocket_test!(|rocket| { + #[rocket::async_test] + async fn missing_user() { + db_test!(|rocket, _conn| { let rocket = rocket.mount("/", routes![handle_auth]); - let client = Client::new(rocket).expect("rocket client"); + let client = Client::tracked(rocket).await.expect("rocket client"); let req = client .get("/") // User that doesn't exist .private_cookie(Cookie::new("vinoteca-auth", "-1")); - let response = req.dispatch(); + let response = req.dispatch().await; assert_eq!(response.status(), Status::Forbidden); }) } - #[test] - fn authorize() { - rocket_test!(|rocket| { + #[rocket::async_test] + async fn authorize() { + db_test!(|rocket, _conn| { let rocket = rocket.mount("/", routes![handle_auth]); - let client = Client::new(rocket).expect("rocket client"); + let client = Client::tracked(rocket).await.expect("rocket client"); let req = client .get("/") .private_cookie(Cookie::new("vinoteca-auth", "1")); - let response = req.dispatch(); + let response = req.dispatch().await; assert_eq!(response.status(), Status::Ok); }) } diff --git a/src/users/handlers.rs b/src/users/handlers.rs index 017facec..f2fdf67c 100644 --- a/src/users/handlers.rs +++ b/src/users/handlers.rs @@ -1,20 +1,22 @@ +use chrono::Utc; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::http::{Cookie, CookieJar, SameSite}; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; +use validator::Validate; + use super::auth::{Auth, COOKIE_NAME}; use super::models::{ChangePasswordForm, ChangeUserForm, LoginForm}; use crate::error::{RestResult, VinotecaError}; use crate::models::{InternalUser, NewUser, User, UserForm}; use crate::schema::users; -use crate::DbConn; - -use chrono::Utc; -use diesel::prelude::*; -use rocket::http::{Cookie, Cookies, SameSite}; -use rocket_contrib::json::Json; -use validator::Validate; +use crate::Db; // TODO: store bad login attempts and lock after 10 #[get("/users")] -pub fn get(auth: Auth, connection: DbConn) -> RestResult { +pub async fn get(auth: Auth, mut conn: Connection) -> RestResult { users::table .filter(users::id.eq(auth.id)) .select(( @@ -24,17 +26,24 @@ pub fn get(auth: Auth, connection: DbConn) -> RestResult { users::created_at, users::last_login, )) - .first(&*connection) + .first(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[post("/users/login", format = "json", data = "
")] -pub fn login(form: Json, mut cookies: Cookies, connection: DbConn) -> RestResult { +pub async fn login( + form: Json, + cookies: &CookieJar<'_>, + + mut conn: Connection, +) -> RestResult { let form = form.into_inner(); let user = users::table .filter(users::email.eq(form.email)) - .first::(&*connection) + .first::(&mut **conn) + .await .map_err(|_| VinotecaError::NotFound("Email not recognized".to_owned()))?; let valid = bcrypt::verify(form.password, &user.hash)?; if !valid { @@ -43,15 +52,20 @@ pub fn login(form: Json, mut cookies: Cookies, connection: DbConn) -> diesel::update(users::table) .filter(users::id.eq(user.id)) .set(users::last_login.eq(Utc::now())) - .execute(&*connection)?; + .execute(&mut **conn) + .await?; - add_auth_cookie(&mut cookies, user.id); + add_auth_cookie(cookies, user.id); Ok(Json(User::from(user))) } #[post("/users", format = "json", data = "")] -pub fn create(form: Json, mut cookies: Cookies, connection: DbConn) -> RestResult { +pub async fn create( + form: Json, + cookies: &CookieJar<'_>, + mut conn: Connection, +) -> RestResult { let form = form.into_inner(); form.validate()?; @@ -63,29 +77,29 @@ pub fn create(form: Json, mut cookies: Cookies, connection: DbConn) -> image: None, hash, }; - diesel::insert_into(users::table) + let user_id = diesel::insert_into(users::table) .values(new_user) .returning(users::id) - .get_result(&*connection) - .map_err(VinotecaError::from) - .and_then(|user_id| { - add_auth_cookie(&mut cookies, user_id); - get(Auth { id: user_id }, connection) - }) + .get_result(&mut **conn) + .await + .map_err(VinotecaError::from)?; + add_auth_cookie(cookies, user_id); + get(Auth { id: user_id }, conn).await } #[post("/users/password", format = "json", data = "")] -pub fn change_password( +pub async fn change_password( auth: Auth, - form: Json, - mut cookies: Cookies, - connection: DbConn, + form: Json>, + cookies: &CookieJar<'_>, + mut conn: Connection, ) -> RestResult<()> { let form = form.into_inner(); form.validate()?; let user = users::table .filter(users::id.eq(auth.id)) - .first::(&*connection)?; + .first::(&mut **conn) + .await?; let valid = bcrypt::verify(form.old_password, &user.hash)?; if !valid { return Err(VinotecaError::Forbidden("Bad password".to_string())); @@ -94,23 +108,24 @@ pub fn change_password( let new_hash = bcrypt::hash(form.new_password, bcrypt::DEFAULT_COST)?; diesel::update(users::table.filter(users::id.eq(auth.id))) .set(users::hash.eq(new_hash)) - .execute(&*connection)?; - add_auth_cookie(&mut cookies, auth.id); + .execute(&mut **conn) + .await?; + add_auth_cookie(cookies, auth.id); Ok(Json(())) } #[post("/users/logout")] -pub fn logout(mut cookies: Cookies) -> RestResult<()> { - cookies.remove_private(Cookie::named(COOKIE_NAME)); - +pub fn logout(cookies: &CookieJar<'_>) -> RestResult<()> { + cookies.remove_private(Cookie::from(COOKIE_NAME)); Ok(Json(())) } #[put("/users", format = "json", data = "")] -pub fn modify_profile( +pub async fn modify_profile( auth: Auth, form: Json, - connection: DbConn, + + mut conn: Connection, ) -> RestResult { let form = form.into_inner(); form.validate()?; @@ -118,41 +133,150 @@ pub fn modify_profile( // Check if exists users::table .filter(users::id.eq(auth.id)) - .first::(&*connection)?; + .first::(&mut **conn) + .await?; diesel::update(users::table.filter(users::id.eq(auth.id))) .set(form) - .execute(&*connection)?; - get(auth, connection) + .execute(&mut **conn) + .await?; + get(auth, conn).await } -fn add_auth_cookie(cookies: &mut Cookies, user_id: i32) { - let cookie = Cookie::build(COOKIE_NAME, user_id.to_string()) +fn add_auth_cookie(cookies: &CookieJar<'_>, user_id: i32) { + let cookie = Cookie::build((COOKIE_NAME, user_id.to_string())) .path("/") .same_site(SameSite::Strict) .http_only(true) .secure(true) - .expires(time::now() + time::Duration::days(14)) - .finish(); + .expires(rocket::time::OffsetDateTime::now_utc() + rocket::time::Duration::days(14)) + .build(); cookies.add_private(cookie); } #[cfg(test)] mod test { - // use super::*; + use super::*; + use crate::testing::PW; - #[test] - #[ignore] - fn logon_adds_cookie() {} + use rocket::http::{ContentType, Status}; + use rocket::local::asynchronous::Client; + use rocket::Build; - #[test] - #[ignore] - fn logoff_removes_cookie() {} + fn mounted_rocket(rocket: rocket::Rocket) -> rocket::Rocket { + rocket.mount( + "/", + routes![get, login, create, change_password, logout, modify_profile], + ) + } + + #[rocket::async_test] + async fn logon_adds_cookie() { + db_test!(|rocket, _conn| { + let rocket = mounted_rocket(rocket); + let client = Client::tracked(rocket).await.expect("rocket client"); + assert!(client.cookies().get(COOKIE_NAME).is_none()); + let req = client.post("/users/login"); + let form = LoginForm { + email: "test@gmail.com".to_owned(), + password: PW.to_owned(), + }; + let mut req = req.header(ContentType::JSON); + req.set_body(serde_json::to_string(&form).unwrap()); + let response = req.dispatch().await; + assert_eq!(response.status(), Status::Ok); + assert!(response.cookies().get(COOKIE_NAME).is_some()); + }) + } - #[test] - #[ignore] - fn new_user() {} + #[rocket::async_test] + async fn logoff_removes_cookie() { + db_test!(|rocket, _conn| { + let rocket = mounted_rocket(rocket); + let client = Client::tracked(rocket).await.expect("rocket client"); + // Login and add cookie + let req = client.post("/users/login"); + let form = LoginForm { + email: "test@gmail.com".to_owned(), + password: PW.to_owned(), + }; + let mut req = req.header(ContentType::JSON); + req.set_body(serde_json::to_string(&form).unwrap()); + let response = req.dispatch().await; + assert_eq!(response.status(), Status::Ok); + assert!(client.cookies().get(COOKIE_NAME).is_some()); + // Logout and remove + let req = client.post("/users/logout"); + let response = req.dispatch().await; + assert_eq!(response.status(), Status::Ok); + assert_eq!(response.cookies().get(COOKIE_NAME).unwrap().value(), ""); + }) + } - #[test] - #[ignore] - fn new_user_bad_input() {} + #[rocket::async_test] + async fn logout_when_not_logged_in_is_no_op() { + db_test!(|rocket, _conn| { + let rocket = mounted_rocket(rocket); + let client = Client::tracked(rocket).await.expect("rocket client"); + assert!(client.cookies().get(COOKIE_NAME).is_none()); + let req = client.post("/users/logout"); + let response = req.dispatch().await; + assert_eq!(response.status(), Status::Ok); + assert!(response.cookies().get(COOKIE_NAME).is_none()); + }) + } + + #[rocket::async_test] + async fn new_user() { + const EMAIL: &str = "test@example.com"; + const NAME: &str = "Test"; + const PASSWORD: &str = "compromised"; + + db_test!(|rocket, _conn| { + let rocket = mounted_rocket(rocket); + let client = Client::tracked(rocket).await.expect("rocket client"); + assert!(client.cookies().get(COOKIE_NAME).is_none()); + let req = client.post("/users"); + let form = UserForm { + email: EMAIL.to_string(), + name: NAME.to_string(), + password: PASSWORD.to_string(), + }; + let mut req = req.header(ContentType::JSON); + req.set_body(serde_json::to_string(&form).unwrap()); + let response = req.dispatch().await; + assert_eq!(response.status(), Status::Ok); + assert!(client.cookies().get(COOKIE_NAME).is_some()); + let req = client.get("/users"); + let response = req.dispatch().await; + assert_eq!(response.status(), Status::Ok); + let user: User = + serde_json::from_str(&response.into_string().await.expect("response body")) + .unwrap(); + assert_eq!(user.email, EMAIL); + assert_eq!(user.name, NAME); + }) + } + + #[rocket::async_test] + async fn new_user_bad_input() { + db_test!(|rocket, _conn| { + let rocket = mounted_rocket(rocket); + let client = Client::tracked(rocket).await.expect("rocket client"); + assert!(client.cookies().get(COOKIE_NAME).is_none()); + let req = client.post("/users"); + let form = UserForm { + email: "test@example.com".to_owned(), + name: "test".to_owned(), + password: "short".to_owned(), + }; + let mut req = req.header(ContentType::JSON); + req.set_body(serde_json::to_string(&form).unwrap()); + let response = req.dispatch().await; + assert_eq!(response.status(), Status::BadRequest); + assert!(client.cookies().get(COOKIE_NAME).is_none()); + let req = client.get("/users"); + let response = req.dispatch().await; + assert_eq!(response.status(), Status::Unauthorized); + }) + } } diff --git a/src/users/models.rs b/src/users/models.rs index 44f59c09..755894cf 100644 --- a/src/users/models.rs +++ b/src/users/models.rs @@ -1,36 +1,37 @@ use crate::schema::users; -use crate::serde::trim_str; +use crate::serde::trim_string; use serde::Deserialize; use typescript_definitions::TypeScriptify; #[derive(Deserialize, TypeScriptify, Validate, Debug)] +#[cfg_attr(test, derive(serde::Serialize))] #[serde(rename_all = "camelCase")] -pub struct LoginForm<'a> { +pub struct LoginForm { #[validate(email(message = "Email %s is invalid"))] - #[serde(deserialize_with = "trim_str")] - pub email: &'a str, + #[serde(deserialize_with = "trim_string")] + pub email: String, #[validate(length(min = 8, message = "Password does not meet minimum length of 8"))] - pub password: &'a str, + pub password: String, } #[derive(Deserialize, TypeScriptify, Validate, Debug)] #[serde(rename_all = "camelCase")] -pub struct ChangePasswordForm<'a> { +pub struct ChangePasswordForm<'r> { #[validate(length(min = 8))] - pub old_password: &'a str, + pub old_password: &'r str, #[validate(length(min = 8))] - pub new_password: &'a str, + pub new_password: &'r str, } #[derive(AsChangeset, Deserialize, TypeScriptify, Validate, Debug)] -#[table_name = "users"] +#[diesel(table_name = users)] #[serde(rename_all = "camelCase")] -pub struct ChangeUserForm<'a> { +pub struct ChangeUserForm { #[validate(email)] - #[serde(deserialize_with = "trim_str")] - pub email: &'a str, + #[serde(deserialize_with = "trim_string")] + pub email: String, #[validate(length(min = 2))] - #[serde(deserialize_with = "trim_str")] - pub name: &'a str, + #[serde(deserialize_with = "trim_string")] + pub name: String, } diff --git a/src/version.rs b/src/version.rs index 15de59e2..1ba44363 100644 --- a/src/version.rs +++ b/src/version.rs @@ -1,4 +1,4 @@ -use rocket_contrib::json::Json; +use rocket::serde::json::Json; use serde::Serialize; use typescript_definitions::TypeScriptify; diff --git a/src/viti_areas.rs b/src/viti_areas.rs index 969dbf03..2d78d511 100644 --- a/src/viti_areas.rs +++ b/src/viti_areas.rs @@ -1,29 +1,31 @@ -use crate::error::{RestResult, VinotecaError}; -use crate::models::{generic, NewVitiArea, VitiArea, VitiAreaForm}; -use crate::query_utils::IntoFirst; -use crate::schema::{purchases, regions, viti_areas, wines}; -use crate::users::Auth; -use crate::DbConn; - use diesel::dsl::sql; use diesel::prelude::*; use diesel::sql_types::{BigInt, Double, Nullable}; -use rocket_contrib::json::Json; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; use serde::Serialize; use typescript_definitions::TypeScriptify; use validator::Validate; +use crate::error::{RestResult, VinotecaError}; +use crate::models::{generic, NewVitiArea, VitiArea, VitiAreaForm}; +use crate::query_utils::IntoFirst; +use crate::schema::{purchases, regions, viti_areas, wines}; +use crate::users::Auth; +use crate::Db; + #[get("/viti-areas?&&&")] -pub fn get( +pub async fn get( auth: Auth, id: Option, name: Option, region_name: Option, region_id: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { - // Inner join because viti areas must have a region id let mut query = viti_areas::table + // Inner join because viti areas must have a region id .inner_join(regions::table) .filter(viti_areas::user_id.eq(auth.id)) .into_boxed(); @@ -41,14 +43,15 @@ pub fn get( } query .select((viti_areas::id, viti_areas::name, regions::id, regions::name)) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/viti-areas/")] -pub fn get_one(auth: Auth, id: i32, connection: DbConn) -> RestResult { - let viti_area = get(auth, Some(id), None, None, None, connection)?; +pub async fn get_one(auth: Auth, id: i32, conn: Connection) -> RestResult { + let viti_area = get(auth, Some(id), None, None, None, conn).await?; viti_area.into_first(&format!("No viticultural area with id {}", id)) } @@ -63,11 +66,11 @@ pub struct VitiAreaStats { } #[get("/viti-areas/stats?&")] -pub fn stats( +pub async fn stats( auth: Auth, id: Option, region_id: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = viti_areas::table .select(( @@ -90,16 +93,17 @@ pub fn stats( query = query.filter(regions::id.eq(region_id)); } query - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/viti-areas/top?")] -pub fn top( +pub async fn top( auth: Auth, limit: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let limit = limit.unwrap_or(10); top_table!( @@ -110,36 +114,36 @@ pub fn top( viti_areas::id, viti_areas::name, limit, - connection + conn ) } #[post("/viti-areas", format = "json", data = "")] -pub fn post( +pub async fn post( auth: Auth, viti_area_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let viti_area_form = viti_area_form.into_inner(); viti_area_form.validate()?; - diesel::insert_into(viti_areas::table) + let viti_area_id = diesel::insert_into(viti_areas::table) .values(NewVitiArea::from((auth, viti_area_form))) .returning(viti_areas::id) - .get_result(&*connection) - .map_err(VinotecaError::from) - .and_then(|viti_area_id| { - get(auth, Some(viti_area_id), None, None, None, connection)? - .into_first("Newly-created viti area") - }) + .get_result(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get(auth, Some(viti_area_id), None, None, None, conn) + .await? + .into_first("Newly-created viti area") } #[put("/viti-areas/", format = "json", data = "")] -pub fn put( +pub async fn put( auth: Auth, id: i32, viti_area_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let viti_area_form = viti_area_form.into_inner(); viti_area_form.validate()?; @@ -149,13 +153,15 @@ pub fn put( .filter(viti_areas::id.eq(id)) .filter(viti_areas::user_id.eq(auth.id)) .select(viti_areas::id) - .first::(&*connection)?; + .first::(&mut **conn) + .await?; diesel::update(viti_areas::table.filter(viti_areas::id.eq(id))) .set(NewVitiArea::from((auth, viti_area_form))) - .execute(&*connection) - .map_err(VinotecaError::from) - .and_then(|_| { - get(auth, Some(id), None, None, None, connection)?.into_first("Edited viti area") - }) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get(auth, Some(id), None, None, None, conn) + .await? + .into_first("Edited viti area") } diff --git a/src/wine_grapes/handlers.rs b/src/wine_grapes/handlers.rs index e8f9db53..2dac2e42 100644 --- a/src/wine_grapes/handlers.rs +++ b/src/wine_grapes/handlers.rs @@ -1,21 +1,23 @@ +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; +use validator::Validate; + use super::models::WineGrapesForm; use super::validation::validate_user_owns_grapes; use crate::error::{RestResult, VinotecaError}; use crate::models::{WineGrape, WineGrapeForm}; use crate::schema::{grapes, wine_grapes}; use crate::users::Auth; -use crate::DbConn; - -use diesel::prelude::*; -use rocket_contrib::json::Json; -use validator::Validate; +use crate::Db; #[get("/wine-grapes?&")] -pub fn get( +pub async fn get( auth: Auth, wine_id: Option, grape_id: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = wine_grapes::table .inner_join(grapes::table) @@ -34,40 +36,44 @@ pub fn get( grapes::name, wine_grapes::wine_id, )) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[post("/wine-grapes", format = "json", data = "")] -pub fn post( +pub async fn post( auth: Auth, wine_grape_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let wine_grape_form = wine_grape_form.into_inner(); wine_grape_form.validate()?; - validate_user_owns_grapes(auth.id, &wine_grape_form.grapes, &connection)?; - let wine_id = wine_grape_form.wine_id; + + validate_user_owns_grapes(auth.id, &wine_grape_form.grapes, &mut **conn).await?; let wine_grapes: Vec = wine_grape_form.into(); // Delete existing wine grapes - let delete_result = diesel::delete(wine_grapes::table.filter(wine_grapes::wine_id.eq(wine_id))) - .execute(&*connection); - if let Err(e) = delete_result { - return Err(VinotecaError::Internal(format!( - "Error deleting existing wine grapes for wine with id {}: {}", - wine_id, e - ))); - } + diesel::delete(wine_grapes::table.filter(wine_grapes::wine_id.eq(wine_id))) + .execute(&mut **conn) + .await + .map_err(|e| { + VinotecaError::Internal(format!( + "Error deleting existing wine grapes for wine with id {}: {}", + wine_id, e + )) + })?; if !wine_grapes.is_empty() { diesel::insert_into(wine_grapes::table) .values(&wine_grapes) - .execute(&*connection)?; + .execute(&mut **conn) + .await?; } - get(auth, Some(wine_id), None, connection) + + get(auth, Some(wine_id), None, conn).await } #[cfg(test)] @@ -75,8 +81,8 @@ mod test { use super::*; use crate::wine_grapes::AssociatedGrape; - #[test] - fn post_many() { + #[rocket::async_test] + async fn post_many() { db_test!(|rocket, connection| { let form = WineGrapesForm { wine_id: 1, @@ -107,15 +113,15 @@ mod test { }, ], }; - let response = post(Auth { id: 1 }, Json(form), connection); + let response = post(Auth { id: 1 }, Json(form), connection).await; assert!(response.is_ok()); let wine_grapes = response.unwrap(); assert_eq!(wine_grapes.len(), 6); }) } - #[test] - fn post_high_percent() { + #[rocket::async_test] + async fn post_high_percent() { db_test!(|rocket, connection| { let form = WineGrapesForm { wine_id: 2, @@ -130,15 +136,15 @@ mod test { }, ], }; - let response = post(Auth { id: 1 }, Json(form), connection); + let response = post(Auth { id: 1 }, Json(form), connection).await; assert!( matches!(response, Err(VinotecaError::BadRequest(desc)) if desc.contains("maximum")) ); }) } - #[test] - fn post_negative_percent() { + #[rocket::async_test] + async fn post_negative_percent() { db_test!(|rocket, connection| { let form = WineGrapesForm { wine_id: 3, @@ -147,13 +153,13 @@ mod test { percent: Some(-3), }], }; - let response = post(Auth { id: 1 }, Json(form), connection); + let response = post(Auth { id: 1 }, Json(form), connection).await; assert!(matches!(response, Err(VinotecaError::BadRequest(_)))); }) } - #[test] - fn post_duplicate() { + #[rocket::async_test] + async fn post_duplicate() { db_test!(|rocket, connection| { let form = WineGrapesForm { wine_id: 1, @@ -172,27 +178,27 @@ mod test { }, ], }; - let response = post(Auth { id: 1 }, Json(form), connection); + let response = post(Auth { id: 1 }, Json(form), connection).await; assert!( matches!(response, Err(VinotecaError::BadRequest(desc)) if desc.contains("Duplicate")) ); }) } - #[test] - fn post_empty_no_op() { + #[rocket::async_test] + async fn post_empty_no_op() { db_test!(|rocket, connection| { let form = WineGrapesForm { wine_id: 3, grapes: Vec::new(), }; - let response = post(Auth { id: 1 }, Json(form), connection); + let response = post(Auth { id: 1 }, Json(form), connection).await; assert!(matches!(response, Ok(Json(wg)) if wg.is_empty() )); }) } - #[test] - fn post_empty_delete_existing() { + #[rocket::async_test] + async fn post_empty_delete_existing() { db_test!(|rocket, connection| { let form = WineGrapesForm { wine_id: 1, @@ -207,16 +213,15 @@ mod test { }, ], }; - let response = post(Auth { id: 1 }, Json(form), connection); + let response = post(Auth { id: 1 }, Json(form), connection).await; assert!(matches!(response, Ok(Json(wg)) if wg.len() == 2)); // Post empty wine grapes for same wine - let connection = DbConn::get_one(&rocket).expect("database connection"); + let connection = Db::get_one(&rocket).await.expect("database connection"); let form = WineGrapesForm { wine_id: 1, grapes: Vec::new(), }; - let response = post(Auth { id: 1 }, Json(form), connection); - dbg!(&response); + let response = post(Auth { id: 1 }, Json(form), connection).await; assert!(matches!(response, Ok(Json(wg)) if wg.is_empty() )); }) } diff --git a/src/wine_grapes/models.rs b/src/wine_grapes/models.rs index 0f2a0989..a8407111 100644 --- a/src/wine_grapes/models.rs +++ b/src/wine_grapes/models.rs @@ -3,6 +3,7 @@ use crate::models::WineGrapeForm; use serde::{Deserialize, Serialize}; use typescript_definitions::TypeScriptify; +use validator::Validate; #[derive(Deserialize, Serialize, Validate, TypeScriptify, Debug)] #[serde(rename_all = "camelCase")] diff --git a/src/wine_grapes/validation.rs b/src/wine_grapes/validation.rs index 834789c7..02c842f6 100644 --- a/src/wine_grapes/validation.rs +++ b/src/wine_grapes/validation.rs @@ -1,12 +1,13 @@ -use super::models::AssociatedGrape; -use crate::error::VinotecaError; -use crate::schema::grapes; -use crate::DbConn; - use diesel::prelude::*; +use diesel_async::{AsyncPgConnection, RunQueryDsl}; +use rocket_db_pools::Connection; use std::collections::HashSet; use validator::ValidationError; +use super::models::AssociatedGrape; +use crate::error::VinotecaError; +use crate::schema::grapes; + pub fn validate_total_percentage(grapes: &[AssociatedGrape]) -> Result<(), ValidationError> { let total_percentage = grapes .iter() @@ -30,10 +31,10 @@ pub fn validate_unique(grapes: &[AssociatedGrape]) -> Result<(), ValidationError Ok(()) } -pub fn validate_user_owns_grapes( +pub async fn validate_user_owns_grapes( user_id: i32, wine_grapes: &[AssociatedGrape], - connection: &DbConn, + pg_conn: &mut AsyncPgConnection, ) -> Result<(), VinotecaError> { let valid_grape_count = grapes::table .filter(grapes::user_id.eq(user_id)) @@ -46,7 +47,8 @@ pub fn validate_user_owns_grapes( ), ) .count() - .get_result::(&**connection)?; + .get_result::(pg_conn) + .await?; if valid_grape_count as usize == wine_grapes.len() { Ok(()) } else { diff --git a/src/wine_types.rs b/src/wine_types.rs index c9443554..697cb42b 100644 --- a/src/wine_types.rs +++ b/src/wine_types.rs @@ -1,21 +1,23 @@ +use diesel::dsl::sql; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; +use validator::Validate; + use crate::error::{RestResult, VinotecaError}; use crate::models::{generic, NewWineType, WineType, WineTypeForm}; use crate::query_utils::IntoFirst; use crate::schema::{purchases, wine_types, wines}; use crate::users::Auth; -use crate::DbConn; - -use diesel::dsl::sql; -use diesel::prelude::*; -use rocket_contrib::json::Json; -use validator::Validate; +use crate::Db; #[get("/wine-types?&")] -pub fn get( +pub async fn get( auth: Auth, id: Option, name: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = wine_types::table .filter(wine_types::user_id.eq(auth.id)) @@ -28,22 +30,23 @@ pub fn get( } query .select((wine_types::id, wine_types::name)) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/wine-types/")] -pub fn get_one(auth: Auth, id: i32, connection: DbConn) -> RestResult { - let wine_types = get(auth, Some(id), None, connection)?; +pub async fn get_one(auth: Auth, id: i32, conn: Connection) -> RestResult { + let wine_types = get(auth, Some(id), None, conn).await?; wine_types.into_first(&format!("No wine type with id {}", id)) } #[get("/wine-types/top?")] -pub fn top( +pub async fn top( auth: Auth, limit: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let limit = limit.unwrap_or(10); top_table!( @@ -54,35 +57,36 @@ pub fn top( wine_types::id, wine_types::name, limit, - connection + conn ) } #[post("/wine-types", format = "json", data = "")] -pub fn post( +pub async fn post( auth: Auth, wine_type_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let wine_type_form = wine_type_form.into_inner(); wine_type_form.validate()?; - diesel::insert_into(wine_types::table) + let wine_type_id = diesel::insert_into(wine_types::table) .values(NewWineType::from((auth, wine_type_form))) .returning(wine_types::id) - .get_result(&*connection) - .map_err(VinotecaError::from) - .and_then(|wine_type_id| { - get(auth, Some(wine_type_id), None, connection)?.into_first("Newly-created wine type") - }) + .get_result(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get(auth, Some(wine_type_id), None, conn) + .await? + .into_first("Newly-created wine type") } #[put("/wine-types/", format = "json", data = "")] -pub fn put( +pub async fn put( auth: Auth, id: i32, wine_type_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let wine_type_form = wine_type_form.into_inner(); wine_type_form.validate()?; @@ -92,11 +96,15 @@ pub fn put( .filter(wine_types::id.eq(id)) .filter(wine_types::user_id.eq(auth.id)) .select(wine_types::id) - .first::(&*connection)?; + .first::(&mut **conn) + .await?; diesel::update(wine_types::table.filter(wine_types::id.eq(id))) .set(wine_types::name.eq(wine_type_form.name)) - .execute(&*connection) - .map_err(VinotecaError::from) - .and_then(|_| get(auth, Some(id), None, connection)?.into_first("Edited wine type")) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get(auth, Some(id), None, conn) + .await? + .into_first("Edited wine type") } diff --git a/src/wines/create.rs b/src/wines/create.rs index 0a3d6f6b..fff7f278 100644 --- a/src/wines/create.rs +++ b/src/wines/create.rs @@ -1,66 +1,72 @@ -use super::image::handle_image; -use super::models::RawWineForm; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket::State; +use rocket_db_pools::Connection; +use validator::Validate; + +// use super::models::RawWineForm; use super::read::get_one; -use crate::config::Config; + use crate::error::{RestResult, VinotecaError}; +use crate::models::WineForm; use crate::models::{NewWine, Wine}; use crate::schema::wines; +use crate::storage::Storage; use crate::users::Auth; -use crate::DbConn; - -use diesel::prelude::*; -use rocket::State; -use validator::Validate; +use crate::Db; -#[post("/wines", data = "")] -pub fn post( +// #[post("/wines", data = "")] +#[post("/wines", data = "", format = "json")] +pub async fn post( auth: Auth, - raw_wine_form: RawWineForm, - connection: DbConn, - config: State, + wine_form: Json, + mut conn: Connection, + storage: &State>, ) -> RestResult { - let wine_form = raw_wine_form.wine_form; - let image = raw_wine_form.image; + // let RawWineForm { wine_form, image } = raw_wine_form.into_inner(); + let wine_form = wine_form.into_inner(); wine_form.validate()?; - diesel::insert_into(wines::table) + let wine_id = diesel::insert_into(wines::table) .values(NewWine::from((auth, wine_form))) .returning(wines::id) - .get_result(&*connection) - .map_err(VinotecaError::from) - .map(|wine_id| { - if let Some(image) = image { - if let Err(e) = handle_image(wine_id, image, &*config.storage, &connection) { - warn!("Error adding image for new wine with id {}: {}", wine_id, e); - }; - } - wine_id - }) - .and_then(|wine_id| get_one(auth, wine_id, connection)) + .get_result(&mut **conn) + .await + .map_err(VinotecaError::from)?; + // if let Some(image) = image { + // warn!("Found an image"); + // if let Err(e) = handle_image(wine_id, image, &***storage, &conn).await { + // warn!("Error adding image for new wine with id {}: {}", wine_id, e); + // }; + // } else { + // warn!("No image found"); + // } + + get_one(auth, wine_id, conn).await } #[cfg(test)] mod test { use super::*; - use crate::config::Config; use crate::models::WineForm; use crate::storage::MockStorage; - use crate::wines::image::Image; use crate::wines::models::RawWineForm; - use crate::DbConn; + use crate::Db; + use rocket::serde::json::Json; use rocket::State; use std::fs::read; use std::path::Path; - fn mock_form() -> RawWineForm { - RawWineForm { - image: Some(Image { - mime_type: "image/jpeg".parse().unwrap(), - data: read(Path::new(env!("CARGO_MANIFEST_DIR")).join("src/test_data/test.jpg")) + fn mock_form() -> Form { + Form::from(RawWineForm { + image: Some( + // mime_type: "image/jpeg".parse().unwrap(), + read(Path::new(env!("CARGO_MANIFEST_DIR")).join("src/test_data/test.jpg")) .expect("Test image"), - }), - wine_form: WineForm { + ), + wine_form: Json(WineForm { description: None, notes: None, rating: Some(5), @@ -72,34 +78,34 @@ mod test { name: None, wine_type_id: 1, is_in_shopping_list: false, - }, - } + }), + }) } - #[test] - fn insert_wine() { + #[rocket::async_test] + async fn insert_wine() { db_test!(|rocket, connection| { let mut mock = MockStorage::new(); mock.expect_create_object() .times(1) .return_const(Ok("uuid123".to_owned())); - let rocket = rocket.manage(Config::new(mock)); - let config = State::from(&rocket).unwrap(); - let response = post(Auth { id: 1 }, mock_form(), connection, config); + let mock_storage: Box = Box::new(mock); + let storage = State::from(&mock_storage); + let response = post(Auth { id: 1 }, mock_form(), connection, &storage).await; assert!(response.is_ok()); }) } - #[test] - fn image_failure_still_saves_wine() { + #[rocket::async_test] + async fn image_failure_still_saves_wine() { db_test!(|rocket, connection| { let mut mock = MockStorage::new(); mock.expect_create_object() .times(1) .return_const(Err(VinotecaError::Internal("No good".to_owned()))); - let rocket = rocket.manage(Config::new(mock)); - let config = State::from(&rocket).unwrap(); - let response = post(Auth { id: 1 }, mock_form(), connection, config); + let mock_storage: Box = Box::new(mock); + let storage = State::from(&mock_storage); + let response = post(Auth { id: 1 }, mock_form(), connection, &storage).await; assert!(response.is_ok()); }) } diff --git a/src/wines/delete.rs b/src/wines/delete.rs index ba404562..01516d56 100644 --- a/src/wines/delete.rs +++ b/src/wines/delete.rs @@ -1,32 +1,41 @@ +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket::State; +use rocket_db_pools::Connection; + use super::image; -use crate::config::Config; use crate::error::{RestResult, VinotecaError}; use crate::schema::wines; +use crate::storage::Storage; use crate::users::Auth; -use crate::DbConn; - -use diesel::prelude::*; -use rocket::State; -use rocket_contrib::json::Json; +use crate::Db; #[delete("/wines/")] -pub fn delete(auth: Auth, id: i32, connection: DbConn, config: State) -> RestResult<()> { +pub async fn delete( + auth: Auth, + id: i32, + mut conn: Connection, + storage: &State>, +) -> RestResult<()> { // Validate is user's wine let image_path = wines::table .filter(wines::id.eq(id)) .filter(wines::user_id.eq(auth.id)) .select(wines::image) - .first::>(&*connection)?; + .first::>(&mut **conn) + .await?; if let Some(image_path) = image_path { - if let Err(e) = image::delete_from_storage(&*config.storage, &image_path) { + if let Err(e) = image::delete_from_storage(&***storage, &image_path).await { warn!("Error deleting image for deleted wine: {:?}", e); }; } diesel::delete(wines::table.filter(wines::id.eq(id))) - .execute(&*connection) - .map(|_| Json(())) - .map_err(VinotecaError::from) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + Ok(Json(())) } #[cfg(test)] @@ -35,25 +44,24 @@ mod test { use crate::storage::MockStorage; use crate::wines::get_one; - use rocket_contrib::json::Json; + use rocket::serde::json::Json; - #[test] - fn delete_removes_wine() { + #[rocket::async_test] + async fn delete_removes_wine() { db_test!(|rocket, connection| { let mut mock = MockStorage::new(); mock.expect_delete_object().times(1).return_const(Ok(())); - let rocket = rocket.manage(Config::new(mock)); - let config = State::from(&rocket).unwrap(); + let mock_storage: Box = Box::new(mock); + let storage = State::from(&mock_storage); let auth = Auth { id: 1 }; - let wines = get_one(auth, 1, connection); + let wines = get_one(auth, 1, connection).await; assert!(matches!(wines, Ok(Json(wines)) if wines.id == 1)); - let connection = DbConn::get_one(&rocket).expect("database connection"); - let response = delete(auth, 1, connection, config); - dbg!(&response); + let connection = Db::get_one(&rocket).await.expect("database connection"); + let response = delete(auth, 1, connection, storage).await; assert!(response.is_ok()); - let connection = DbConn::get_one(&rocket).expect("database connection"); - let res = get_one(auth, 1, connection); + let connection = Db::get_one(&rocket).await.expect("database connection"); + let res = get_one(auth, 1, connection).await; assert!(matches!(res, Err(VinotecaError::NotFound(_)))); }) } diff --git a/src/wines/image.rs b/src/wines/image.rs index ea82fced..9999b412 100644 --- a/src/wines/image.rs +++ b/src/wines/image.rs @@ -1,93 +1,97 @@ -use super::{update::validate_owns_wine, Rotation, RotationForm}; -use crate::config::Config; -use crate::error::{RestResult, VinotecaError}; -use crate::schema::wines; -use crate::storage::Storage; -use crate::users::Auth; -use crate::DbConn; - use diesel::prelude::*; +use diesel_async::{AsyncPgConnection, RunQueryDsl}; use image::GenericImageView; +use rocket::data::Capped; +use rocket::serde::json::Json; use rocket::State; -use rocket_contrib::json::Json; -use rocket_multipart_form_data::mime::{self, Mime}; +use rocket_db_pools::Connection; use std::io::{BufReader, Cursor}; -pub struct Image { - pub mime_type: Mime, - pub data: Vec, -} +use super::{update::validate_owns_wine, Rotation, RotationForm}; +use crate::error::{RestResult, VinotecaError}; +use crate::schema::wines; +use crate::storage::Storage; +use crate::users::Auth; +use crate::Db; const WINE_DIR: &str = "wine_images"; const MAX_IMAGE_DIM: u32 = 1280; /// Auth must be handled before this function is called -pub fn handle_image( +pub async fn handle_image( wine_id: i32, - image: Image, + image: Vec, storage: &dyn Storage, - connection: &DbConn, + conn: &mut AsyncPgConnection, ) -> Result { // Read in image and fix orientation based on EXIF data - let image = reformat_image(image)?; + let image = reformat_image(&image)?; // AWS - let path = storage.create_object(WINE_DIR, &image, "image/jpeg")?; - - diesel::update(wines::table) - .filter(wines::id.eq(wine_id)) - .set(wines::image.eq(&path)) - .execute(&**connection)?; - - Ok(path) + // let path = storage + // .create_object(WINE_DIR, &image, "image/jpeg") + // .await?; + todo!() + // let ret = path.clone(); + // diesel::update(wines::table) + // .filter(wines::id.eq(wine_id)) + // .set(wines::image.eq(&path)) + // .execute(conn) + // .await?; + + // Ok(ret) } #[post("/wines//image", data = "")] -pub fn post( +pub async fn post( auth: Auth, id: i32, - image: Image, - connection: DbConn, - config: State, + image: Capped>, + mut conn: Connection, + storage: &State>, ) -> RestResult { - validate_owns_wine(auth, id, &connection)?; + validate_owns_wine(auth, id, &mut **conn).await?; + let existing_image_path = get_image_path(auth, id, &mut **conn).await?; - let existing_image_path = get_image_path(auth, id, &connection)?; - - let path = handle_image(id, image, &*config.storage, &connection)?; + let path = handle_image(id, image.into_inner(), &***storage, &mut conn).await?; if let Some(existing_image_path) = existing_image_path { // Delete old image after we upload the new one - config.storage.delete_object(&existing_image_path)?; + // storage.delete_object(&existing_image_path).await?; } Ok(Json(path)) } #[patch("/wines//image", format = "json", data = "")] -pub fn rotate( +pub async fn rotate( auth: Auth, id: i32, rotation_form: Json, - connection: DbConn, - config: State, + mut conn: Connection, + storage: &State>, ) -> RestResult { - let path = get_image_path(auth, id, &connection).and_then(|file_path| { - file_path.ok_or_else(|| VinotecaError::NotFound(format!("No wine with id {}", id))) - })?; + let path = get_image_path(auth, id, &mut **conn) + .await? + .ok_or_else(|| VinotecaError::NotFound(format!("No wine with id {}", id)))?; let rotation = rotation_form.into_inner().rotation; let full_path = format!("{}/{}", WINE_DIR, path); - let image_bytes = config.storage.get_object(&full_path)?; + let image_bytes = storage.get_object(&full_path).await?; let rotated_image = rotate_image(image_bytes, rotation)?; info!( "Updated rotation of image at {} by rotation {:?}", full_path, rotation, ); - config.storage.update_object(&full_path, &rotated_image)?; + // storage.update_object(&full_path, &rotated_image).await?; Ok(Json(path)) } #[delete("/wines//image")] -pub fn delete(auth: Auth, id: i32, connection: DbConn, config: State) -> RestResult<()> { - let file_path = get_image_path(auth, id, &connection)?; +pub async fn delete( + auth: Auth, + id: i32, + mut conn: Connection, + storage: &State>, +) -> RestResult<()> { + let file_path = get_image_path(auth, id, &mut **conn).await?; match file_path { Some(file_path) => { @@ -97,60 +101,52 @@ pub fn delete(auth: Auth, id: i32, connection: DbConn, config: State) -> diesel::update(wines::table) .filter(wines::id.eq(id)) .set(wines::image.eq::>(None)) - .execute(&*connection)?; - delete_from_storage(&*config.storage, &file_path).map(Json) + .execute(&mut **conn) + .await?; + delete_from_storage(&***storage, &file_path).await?; + Ok(Json(())) } None => Ok(Json(())), } } -fn get_image_path( +async fn get_image_path( auth: Auth, id: i32, - connection: &DbConn, + pg_conn: &mut AsyncPgConnection, ) -> Result, VinotecaError> { Ok(wines::table .filter(wines::user_id.eq(auth.id)) .filter(wines::id.eq(id)) .select(wines::image) - .first::>(&**connection)?) + .first::>(pg_conn) + .await?) } -pub fn delete_from_storage(storage: &dyn Storage, path: &str) -> Result<(), VinotecaError> { - storage.delete_object(&format!("{}/{}", WINE_DIR, path)) +pub async fn delete_from_storage(storage: &dyn Storage, path: &str) -> Result<(), VinotecaError> { + todo!() + // storage + // .delete_object(&format!("{}/{}", WINE_DIR, path)) + // .await } -fn get_exif(mime_type: &Mime, raw: Vec) -> Option { - match (mime_type.type_(), mime_type.subtype()) { - (mime::IMAGE, mime::JPEG) => { - info!("Trying to extract EXIF data from JPEG"); - let mut reader = BufReader::new(raw.as_slice()); - exif::get_exif_attr_from_jpeg(&mut reader) +fn get_exif(raw: &[u8]) -> Option { + let mut reader = BufReader::new(raw); + exif::get_exif_attr_from_jpeg(&mut reader) + .map_err(|e| { + warn!("Failed to extract exif attr from JPEG: {:?}", e); + e + }) + .ok() + .and_then(|exif_attr| { + exif::Reader::new() + .read_raw(exif_attr) .map_err(|e| { - warn!("Failed to extract exif attr from JPEG: {:?}", e); + warn!("Failed to read exif attr from JPEG: {:?}", e); e }) .ok() - .and_then(|exif_attr| { - exif::Reader::new() - .read_raw(exif_attr) - .map_err(|e| { - warn!("Failed to read exif attr from JPEG: {:?}", e); - e - }) - .ok() - }) - } - // Try reading with other types of image files, even though should only work with TIFF - (mime::IMAGE, _) => match exif::Reader::new().read_raw(raw) { - Ok(exif) => Some(exif), - Err(e) => { - error!("Error creating exif reader: {:?}", e); - None - } - }, - _ => None, - } + }) } fn handle_exif( @@ -160,8 +156,8 @@ fn handle_exif( let orientation_field = exif.get_field(exif::Tag::Orientation, exif::In::PRIMARY); if let Some(orientation_field) = orientation_field { let orientation = match &orientation_field.value { - exif::Value::Short(v) => v.get(0).map(|n| n.to_owned()), - exif::Value::Byte(v) => v.get(0).map(|n| n.to_owned() as u16), + exif::Value::Short(v) => v.iter().next().map(|n| n.to_owned()), + exif::Value::Byte(v) => v.iter().next().map(|n| n.to_owned() as u16), _ => None, }; if let Some(orientation) = orientation { @@ -192,16 +188,11 @@ fn handle_exif( } } -fn reformat_image(image: Image) -> Result, VinotecaError> { - let decoded_image = image::io::Reader::new(Cursor::new(image.data.as_slice())) +fn reformat_image(raw: &[u8]) -> Result, VinotecaError> { + let decoded_image = image::io::Reader::new(Cursor::new(raw)) .with_guessed_format()? .decode()?; - // Unpack to avoid clone - let Image { - mime_type, - data: raw, - } = image; - let (mut decoded_image, orientation) = if let Some(exif) = get_exif(&mime_type, raw) { + let (mut decoded_image, orientation) = if let Some(exif) = get_exif(raw) { handle_exif(decoded_image, exif) } else { (decoded_image, None) @@ -227,8 +218,8 @@ fn reformat_image(image: Image) -> Result, VinotecaError> { ) .map_err(|e| { warn!( - "Failed to reformat image: {:?}. MimeType: {:?}, Orientation: {:?}", - e, mime_type, orientation + "Failed to reformat image: {:?}. Orientation: {:?}", + e, orientation ); VinotecaError::Internal("Failed to reformat image".to_owned()) })?; diff --git a/src/wines/middleware.rs b/src/wines/middleware.rs deleted file mode 100644 index 35bc0b50..00000000 --- a/src/wines/middleware.rs +++ /dev/null @@ -1,178 +0,0 @@ -use super::image::Image; -use super::models::RawWineForm; -use crate::error::VinotecaError; -use crate::models::WineForm; - -use rocket::data::{FromDataSimple, Outcome}; -use rocket::http::Status; -use rocket::{Data, Outcome::*, Request}; -use rocket_multipart_form_data::{ - mime, MultipartFormData, MultipartFormDataField, MultipartFormDataOptions, RawField, -}; - -impl FromDataSimple for RawWineForm { - type Error = VinotecaError; - - fn from_data(request: &Request, data: Data) -> Outcome { - // Create form parser - let mut options = MultipartFormDataOptions::new(); - options.allowed_fields.push( - MultipartFormDataField::raw("image") - .size_limit(16 * 1024 * 1024) // 16 MB - .content_type_by_string(Some(mime::IMAGE_STAR)) - .unwrap(), - ); - options - .allowed_fields - .push(MultipartFormDataField::text("wine_form").content_type(Some(mime::STAR_STAR))); - - // Check if content type is correct - let content_type = match request.content_type() { - Some(content_type) => content_type, - _ => { - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest( - "Incorrect content-type, should be 'multipart/form-data'".to_owned(), - ), - )) - } - }; - - // Parse form - let mut multipart_form = match MultipartFormData::parse(content_type, data, options) { - Ok(m) => m, - Err(e) => { - error!("Failed to parse multipart form. {:?}", e); - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest(format!("Failed to parse multipart form. {:?}", e)), - )); - } - }; - // Check for wine_form field - let wine_form_json = match multipart_form.texts.remove("wine_form") { - Some(wine_form_json) => wine_form_json, - _ => { - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest("Missing required field 'wine_form'".to_owned()), - )) - } - }; - // Check for image field - let image_part: Option> = multipart_form.raw.remove("image"); - // Verify only one text field - let wine_form = match wine_form_json { - texts if texts.len() == 1 => match serde_json::from_str::(&texts[0].text) { - Ok(parsed) => parsed, - Err(e) => { - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest(format!( - "Failed to parse wine form JSON: {:?}", - e - )), - )) - } - }, - _text => { - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest("Extra text fields supplied".to_owned()), - )) - } - }; - // Verify only one image provided - let image = match image_part { - Some(raw) if raw.len() == 1 => { - let raw = &raw[0]; - Some(Image { - // FIXME: clone - data: raw.raw.clone(), - // TODO: determine if default is good - mime_type: raw - .content_type - .clone() - .unwrap_or_else(|| "image/png".parse().expect("PNG mime")), - }) - } - Some(_raw) => { - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest("Invalid number of image fields supplied".to_owned()), - )) - } - None => None, - }; - - Success(RawWineForm { wine_form, image }) - } -} - -impl FromDataSimple for Image { - type Error = VinotecaError; - - fn from_data(request: &Request, data: Data) -> Outcome { - let mut options = MultipartFormDataOptions::new(); - options.allowed_fields.push( - MultipartFormDataField::raw("image") - .size_limit(16 * 1024 * 1024) // 16 MB - .content_type_by_string(Some(mime::IMAGE_STAR)) - .unwrap(), - ); - - // Check if content type is correct - let content_type = match request.content_type() { - Some(content_type) => content_type, - _ => { - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest( - "Incorrect content-type, should be 'multipart/form-data'".to_owned(), - ), - )) - } - }; - - // Parse form - let mut multipart_form = match MultipartFormData::parse(content_type, data, options) { - Ok(m) => m, - Err(e) => { - error!("Failed to parse multipart form. {:?}", e); - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest(format!("Failed to parse multipart form. {:?}", e)), - )); - } - }; - // Check for image field. `remove()` get owned `RawField` - let raw_field = match multipart_form.raw.remove("image") { - Some(field) => field, - _ => { - return Failure(( - Status::BadRequest, - VinotecaError::BadRequest("Missing required field 'image'".to_owned()), - )) - } - }; - // Verify only one image provided - if raw_field.len() == 1 { - let raw = &raw_field[0]; - Success(Image { - // FIXME: clone - data: raw.raw.clone(), - // TODO: determine if default is good - mime_type: raw - .content_type - .clone() - .unwrap_or_else(|| "image/jpeg".parse().expect("JPEG mime")), - }) - } else { - Failure(( - Status::BadRequest, - VinotecaError::BadRequest("Invalid number of image fields supplied".to_owned()), - )) - } - } -} diff --git a/src/wines/mod.rs b/src/wines/mod.rs index ef6f21de..955b74bb 100644 --- a/src/wines/mod.rs +++ b/src/wines/mod.rs @@ -1,13 +1,12 @@ mod create; mod delete; pub mod image; -mod middleware; mod models; mod read; mod update; pub use self::create::*; -pub use self::delete::*; +pub use self::delete::delete as delete_; pub use self::models::{InventoryWine, Rotation, RotationForm, WineCount, WinePatchForm}; pub use self::read::*; pub use self::update::*; diff --git a/src/wines/models.rs b/src/wines/models.rs index c377ac3e..742285c5 100644 --- a/src/wines/models.rs +++ b/src/wines/models.rs @@ -1,15 +1,20 @@ -use super::image::Image; +// use super::image::Image; use crate::models::WineForm; use chrono::NaiveDate; +// use rocket::fs::TempFile; +use rocket::serde::json::Json; use serde::{Deserialize, Serialize}; use typescript_definitions::TypeScriptify; +// FIXME: better name for this +#[derive(FromForm)] pub struct RawWineForm { /// JSON data for database - pub wine_form: WineForm, - /// raw submitted wine image - pub image: Option, + pub wine_form: Json, + // raw submitted wine image + // FIXME: data limits + // pub image: Option>, } #[derive(Serialize, TypeScriptify, Debug)] diff --git a/src/wines/read.rs b/src/wines/read.rs index 1475dfac..44639042 100644 --- a/src/wines/read.rs +++ b/src/wines/read.rs @@ -1,23 +1,26 @@ +use diesel::dsl::count; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use rocket::serde::json::Json; +use rocket_db_pools::Connection; + use super::models::{InventoryWine, WineCount}; + use crate::error::{RestResult, VinotecaError}; use crate::models::Wine; use crate::query_utils::{lower, IntoFirst}; use crate::schema::{colors, producers, recent_purchases, regions, viti_areas, wine_types, wines}; use crate::users::Auth; -use crate::DbConn; - -use diesel::dsl::count; -use diesel::prelude::*; -use rocket_contrib::json::Json; +use crate::Db; fn add_wildcards(query: &str) -> String { - format!("%{}%", query) + format!("%{query}%") } /// Contains all information used in the wine table #[allow(clippy::too_many_arguments)] #[get("/wines?&&&&&&&&&&")] -pub fn get( +pub async fn get( auth: Auth, // Exact match parameters id: Option, @@ -33,7 +36,7 @@ pub fn get( region: Option, viti_area: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = wines::table .inner_join(producers::table.inner_join(regions::table)) @@ -100,13 +103,14 @@ pub fn get( wines::image, wines::is_in_shopping_list, )) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/wines/")] -pub fn get_one(auth: Auth, id: i32, connection: DbConn) -> RestResult { +pub async fn get_one(auth: Auth, id: i32, conn: Connection) -> RestResult { let wines = get( auth, Some(id), @@ -120,13 +124,14 @@ pub fn get_one(auth: Auth, id: i32, connection: DbConn) -> RestResult { None, None, None, - connection, - )?; + conn, + ) + .await?; wines.into_first(&format!("No wine with id {}", id)) } #[get("/wines/inventory")] -pub fn inventory(auth: Auth, connection: DbConn) -> RestResult> { +pub async fn inventory(auth: Auth, mut conn: Connection) -> RestResult> { wines::table .inner_join(producers::table.inner_join(regions::table)) .inner_join(colors::table) @@ -150,7 +155,8 @@ pub fn inventory(auth: Auth, connection: DbConn) -> RestResult(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } @@ -160,14 +166,14 @@ fn wrap_in_wildcards(filter_str: &str) -> String { } #[get("/wines/search?&&&&")] -pub fn search( +pub async fn search( auth: Auth, color_like: Option, wine_type_like: Option, producer_like: Option, region_like: Option, viti_area_like: Option, - connection: DbConn, + mut conn: Connection, ) -> RestResult> { let mut query = wines::table .inner_join(producers::table.inner_join(regions::table)) @@ -222,21 +228,21 @@ pub fn search( wines::image, wines::is_in_shopping_list, )) - .load::(&*connection) + .load::(&mut **conn) + .await .map(Json) .map_err(VinotecaError::from) } #[get("/wines/count")] -pub fn varieties(auth: Auth, connection: DbConn) -> Json { - let res = wines::table +pub async fn varieties(auth: Auth, mut conn: Connection) -> Json { + let count = wines::table .filter(wines::user_id.eq(auth.id)) .select(count(wines::id)) - .first(&*connection); - let total_liters = WineCount { - count: res.unwrap_or(0), - }; - Json(total_liters) + .first(&mut **conn) + .await + .unwrap_or(0); + Json(WineCount { count }) } #[cfg(test)] @@ -244,23 +250,23 @@ mod test { use super::super::models::RawWineForm; use super::super::post; use super::*; - use crate::config::Config; use crate::models::WineForm; - use crate::storage::MockStorage; - use crate::DbConn; + use crate::storage::{MockStorage, Storage}; + use crate::Db; + use rocket::form::Form; use rocket::State; - #[test] - fn wine_without_purchases_appears_in_inventory() { + #[rocket::async_test] + async fn wine_without_purchases_appears_in_inventory() { db_test!(|rocket, connection| { let auth = Auth { id: 1 }; let mock = MockStorage::new(); - let rocket = rocket.manage(Config::new(mock)); - let config = State::from(&rocket).unwrap(); - let form = RawWineForm { + let mock_storage: Box = Box::new(mock); + let storage = State::from(&mock_storage); + let form = Form::from(RawWineForm { image: None, - wine_form: WineForm { + wine_form: Json(WineForm { description: None, notes: None, rating: Some(5), @@ -272,14 +278,14 @@ mod test { name: None, wine_type_id: 1, is_in_shopping_list: false, - }, - }; - let wine_response = post(auth, form, connection, config); + }), + }); + let wine_response = post(auth, form, connection, storage).await; assert!(wine_response.is_ok()); let wine_id = wine_response.unwrap().id; - let connection = DbConn::get_one(&rocket).expect("database connection"); - let inventory_response = inventory(auth, connection); + let connection = Db::get_one(&rocket).await.expect("database connection"); + let inventory_response = inventory(auth, connection).await; assert!(inventory_response.is_ok()); let inventory = inventory_response.unwrap().into_inner(); assert!(inventory.iter().any(|w| w.id == wine_id)); diff --git a/src/wines/update.rs b/src/wines/update.rs index 18b8bd64..a6b5a90e 100644 --- a/src/wines/update.rs +++ b/src/wines/update.rs @@ -1,34 +1,39 @@ +use diesel::prelude::*; +use diesel_async::{AsyncPgConnection, RunQueryDsl}; +// use rocket::form::Form; +use rocket::serde::json::Json; +use rocket::State; +use rocket_db_pools::Connection; +use validator::Validate; + use super::get_one; -use super::image::handle_image; -use super::models::{RawWineForm, WinePatchForm}; -use crate::config::Config; +// use super::image::handle_image; +use super::models::WinePatchForm; use crate::error::{RestResult, VinotecaError}; -use crate::models::{NewWine, Wine}; +use crate::models::{NewWine, Wine, WineForm}; use crate::schema::wines; +use crate::storage::Storage; use crate::users::Auth; -use crate::DbConn; - -use diesel::prelude::*; -use rocket::State; -use rocket_contrib::json::Json; -use validator::Validate; +use crate::Db; +// #[patch("/wines/", format = "json", data = "")] #[patch("/wines/", format = "json", data = "")] -pub fn patch( +pub async fn patch( auth: Auth, id: i32, wine_patch_form: Json, - connection: DbConn, + mut conn: Connection, ) -> RestResult { let wine_patch_form = wine_patch_form.into_inner(); - validate_owns_wine(auth, id, &connection)?; + validate_owns_wine(auth, id, &mut **conn).await?; match wine_patch_form { WinePatchForm::Inventory(inventory) if inventory >= 0 => { diesel::update(wines::table.filter(wines::id.eq(id))) .set(wines::inventory.eq(inventory)) - .execute(&*connection) - .map_err(VinotecaError::from) - .and_then(|_| get_one(auth, id, connection)) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get_one(auth, id, conn).await } WinePatchForm::Inventory(_invalid_inventory) => Err(VinotecaError::BadRequest( "Invalid inventory value".to_owned(), @@ -36,47 +41,52 @@ pub fn patch( WinePatchForm::IsInShoppingList(is_in_shopping_list) => { diesel::update(wines::table.filter(wines::id.eq(id))) .set(wines::is_in_shopping_list.eq(is_in_shopping_list)) - .execute(&*connection) - .map_err(VinotecaError::from) - .and_then(|_| get_one(auth, id, connection)) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + get_one(auth, id, conn).await } } } -#[put("/wines/", data = "")] -pub fn put( +// #[put("/wines/", data = "")] +#[put("/wines/", format = "json", data = "")] +pub async fn put( auth: Auth, id: i32, - raw_wine_form: RawWineForm, - connection: DbConn, - config: State, + wine_form: Json, + mut conn: Connection, + storage: &State>, ) -> RestResult { - let wine_form = raw_wine_form.wine_form; - let image = raw_wine_form.image; + let wine_form = wine_form.into_inner(); wine_form.validate()?; - validate_owns_wine(auth, id, &connection)?; + validate_owns_wine(auth, id, &mut **conn).await?; // TODO: editing an image should be handled separately diesel::update(wines::table.filter(wines::id.eq(id))) .set(NewWine::from((auth, wine_form))) - .execute(&*connection) - .map_err(VinotecaError::from) - .map(|_| { - if let Some(image) = image { - if let Err(e) = handle_image(id, image, &*config.storage, &connection) { - warn!("Error updating image for wine with id {}: {}", id, e); - } - } - }) - .and_then(|_| get_one(auth, id, connection)) + .execute(&mut **conn) + .await + .map_err(VinotecaError::from)?; + // if let Some(image) = image { + // if let Err(e) = handle_image(id, image, &***storage, &conn).await { + // warn!("Error updating image for wine with id {}: {}", id, e); + // } + // } + get_one(auth, id, conn).await } -pub fn validate_owns_wine(auth: Auth, id: i32, connection: &DbConn) -> Result<(), VinotecaError> { +pub async fn validate_owns_wine( + auth: Auth, + id: i32, + conn: &mut AsyncPgConnection, +) -> Result<(), VinotecaError> { wines::table .filter(wines::id.eq(id)) .filter(wines::user_id.eq(auth.id)) .select(wines::id) - .first::(&**connection)?; + .first::(conn) + .await?; Ok(()) } @@ -84,64 +94,72 @@ pub fn validate_owns_wine(auth: Auth, id: i32, connection: &DbConn) -> Result<() mod test { use super::*; use crate::storage::MockStorage; - use crate::DbConn; + use crate::Db; use crate::{models::WineForm, wines::get_one}; - #[test] - fn validate_owns_wine_fails() { + #[rocket::async_test] + async fn validate_owns_wine_fails() { db_test!(|rocket, connection| { // Invalid user id - let res = validate_owns_wine(Auth { id: -1 }, 1, &connection); + let res = connection + .run(|c| validate_owns_wine(Auth { id: -1 }, 1, c)) + .await; assert!(matches!(res, Err(VinotecaError::NotFound(_)))); }) } - #[test] - fn validate_owns_wine_succeeds() { + #[rocket::async_test] + async fn validate_owns_wine_succeeds() { db_test!(|rocket, connection| { - let res = validate_owns_wine(Auth { id: 1 }, 1, &connection); + let res = connection + .run(|c| validate_owns_wine(Auth { id: 1 }, 1, c)) + .await; assert!(res.is_ok()); }) } - #[test] - fn nullifying_field() { + #[rocket::async_test] + async fn nullifying_field() { db_test!(|rocket, connection| { let auth = Auth { id: 1 }; - let wine1 = get_one(auth, 1, connection).unwrap(); + let wine1 = get_one(auth, 1, connection).await.unwrap(); assert!(wine1.description.is_none()); let mut form = WineForm::from(wine1.into_inner()); form.description = Some("pleasant".to_owned()); // Mock storage setup let mock = MockStorage::new(); - // mock.expect_delete_object().times(1).return_const(Ok(())); - let rocket = rocket.manage(Config::new(mock)); + let mock_storage: Box = Box::new(mock); + let storage = State::from(&mock_storage); + let connection = Db::get_one(&rocket).await.expect("database connection"); // Add description let wine2 = put( auth, 1, - RawWineForm { - image: None, - wine_form: form, - }, - DbConn::get_one(&rocket).unwrap(), - State::from(&rocket).unwrap(), + Form::from(RawWineForm { + // image: None, + wine_form: Json(form), + }), + connection, + storage, ) + .await .unwrap(); assert_eq!(wine2.description, Some("pleasant".to_owned())); // Nullify description let mut null_form = WineForm::from(wine2.into_inner()); null_form.description = None; + let connection = Db::get_one(&rocket).await.expect("database connection"); let wine3 = put( auth, 1, - RawWineForm { - image: None, - wine_form: null_form, - }, - DbConn::get_one(&rocket).unwrap(), - State::from(&rocket).unwrap(), + Form::from(RawWineForm { + // image: None, + wine_form: Json(null_form), + }), + connection, + storage, ) + .await .unwrap(); assert!(wine3.description.is_none()); }); diff --git a/web/.nsprc b/web/.nsprc new file mode 100644 index 00000000..2a4d0df8 --- /dev/null +++ b/web/.nsprc @@ -0,0 +1,3 @@ +{ + "1070289": "No upgrade path at this time" +} diff --git a/web/front_end/about/Changelog.tsx b/web/front_end/about/Changelog.tsx index d2f56653..c7a95b9c 100644 --- a/web/front_end/about/Changelog.tsx +++ b/web/front_end/about/Changelog.tsx @@ -12,6 +12,14 @@ export const Changelog: React.FC = () => {

Changelog

+
6.3.0
+
    +
  • Update rocket to version 0.5-rc2, which supports async I/O
  • +
+ +
6.2.10
+

Upgrade Rust dependencies

+
6.2.9
  • Fix total price calculation in purchases by year chart
  • diff --git a/web/package-lock.json b/web/package-lock.json index 3df41bfb..60241f0d 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,7 +8,6 @@ "license": "MIT", "dependencies": { "@gatsbyjs/reach-router": "^1.3.7", - "@types/gatsbyjs__reach-router": "^1.3.0", "chart.js": "^2.9.4", "date-fns": "^2.28.0", "materialize-css": "^1.0.0", @@ -17,8 +16,9 @@ }, "devDependencies": { "@testing-library/jest-dom": "^5.16.3", - "@testing-library/react": "^12.1.4", + "@testing-library/react": "^13.0.0", "@types/chart.js": "^2.9.36", + "@types/gatsbyjs__reach-router": "^1.3.0", "@types/materialize-css": "^1.0.11", "@types/pickadate": "^3.5.32", "@types/react": "^17.0.43", @@ -1767,21 +1767,30 @@ } }, "node_modules/@testing-library/react": { - "version": "12.1.4", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.4.tgz", - "integrity": "sha512-jiPKOm7vyUw311Hn/HlNQ9P8/lHNtArAx0PisXyFixDDvfl8DbD6EUdbshK5eqauvBSvzZd19itqQ9j3nferJA==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.3.0.tgz", + "integrity": "sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.0.0", - "@types/react-dom": "*" + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" }, "engines": { "node": ">=12" }, "peerDependencies": { - "react": "*", - "react-dom": "*" + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/@types/react-dom": { + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.5.tgz", + "integrity": "sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==", + "dev": true, + "dependencies": { + "@types/react": "*" } }, "node_modules/@tootallnate/once": { @@ -1879,6 +1888,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@types/gatsbyjs__reach-router/-/gatsbyjs__reach-router-1.3.0.tgz", "integrity": "sha512-7dfI9peaJk7TuIIaW8r6r8UaobvR+zqyc/x8pQpqwOFHCiLXl49TUxMoapFv1BQFAbT9UKrvlsijJk7X5r18lQ==", + "dev": true, "dependencies": { "@types/reach__router": "*" } @@ -1999,12 +2009,14 @@ "node_modules/@types/prop-types": { "version": "15.7.4", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", - "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true }, "node_modules/@types/reach__router": { "version": "1.3.10", "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.10.tgz", "integrity": "sha512-iHAFGaVOrWi00/q7oBybggGsz5TOmwOW4M1H9sT7i9lly4qFC8XOgsdf6jUsoaOz2sknFHALEtZqCoDbokdJ2Q==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -2013,6 +2025,7 @@ "version": "17.0.43", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2031,7 +2044,8 @@ "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true }, "node_modules/@types/sizzle": { "version": "2.3.2", @@ -3928,7 +3942,8 @@ "node_modules/csstype": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", + "dev": true }, "node_modules/dashdash": { "version": "1.14.1", @@ -13452,14 +13467,25 @@ } }, "@testing-library/react": { - "version": "12.1.4", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.4.tgz", - "integrity": "sha512-jiPKOm7vyUw311Hn/HlNQ9P8/lHNtArAx0PisXyFixDDvfl8DbD6EUdbshK5eqauvBSvzZd19itqQ9j3nferJA==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.3.0.tgz", + "integrity": "sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ==", "dev": true, "requires": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.0.0", - "@types/react-dom": "*" + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "dependencies": { + "@types/react-dom": { + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.5.tgz", + "integrity": "sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==", + "dev": true, + "requires": { + "@types/react": "*" + } + } } }, "@tootallnate/once": { @@ -13554,6 +13580,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@types/gatsbyjs__reach-router/-/gatsbyjs__reach-router-1.3.0.tgz", "integrity": "sha512-7dfI9peaJk7TuIIaW8r6r8UaobvR+zqyc/x8pQpqwOFHCiLXl49TUxMoapFv1BQFAbT9UKrvlsijJk7X5r18lQ==", + "dev": true, "requires": { "@types/reach__router": "*" } @@ -13674,12 +13701,14 @@ "@types/prop-types": { "version": "15.7.4", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", - "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true }, "@types/reach__router": { "version": "1.3.10", "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.10.tgz", "integrity": "sha512-iHAFGaVOrWi00/q7oBybggGsz5TOmwOW4M1H9sT7i9lly4qFC8XOgsdf6jUsoaOz2sknFHALEtZqCoDbokdJ2Q==", + "dev": true, "requires": { "@types/react": "*" } @@ -13688,6 +13717,7 @@ "version": "17.0.43", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", + "dev": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -13706,7 +13736,8 @@ "@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true }, "@types/sizzle": { "version": "2.3.2", @@ -15130,7 +15161,8 @@ "csstype": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", + "dev": true }, "dashdash": { "version": "1.14.1", diff --git a/web/package.json b/web/package.json index 5faf54aa..a253bc70 100644 --- a/web/package.json +++ b/web/package.json @@ -20,7 +20,7 @@ }, "devDependencies": { "@testing-library/jest-dom": "^5.16.3", - "@testing-library/react": "^12.1.4", + "@testing-library/react": "^13.0.0", "@types/chart.js": "^2.9.36", "@types/gatsbyjs__reach-router": "^1.3.0", "@types/materialize-css": "^1.0.11", @@ -58,6 +58,7 @@ ], "scripts": { "analyze-bundle": "webpack-bundle-analyzer webpack.stats.json ./static -p 8889", + "audit": "better-npm-audit audit", "build": "webpack --mode production", "lint": "eslint '*/**/*.{js,ts,tsx}' --fix", "profile": "webpack --profile --json --mode production > webpack.stats.json",