diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0018273..aae9868 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,8 @@ on: push: - branches: [ "master" ] + branches: ["master"] pull_request: - branches: [ "master" ] + branches: ["*"] name: Continuous integration @@ -36,27 +36,51 @@ jobs: - nightly steps: - - uses: actions/checkout@v4 - - - name: Cache Dependencies - uses: Swatinem/rust-cache@v2 - - - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - - - name: Build - run: cargo build --verbose - - - name: Run tests - run: cargo test --verbose + - uses: actions/checkout@v4 + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose check-examples: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: Check examples + run: cargo check --manifest-path=./examples/Cargo.toml + + check-formatting: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: Check formatting + run: cargo fmt --all -- --check + + check-clippy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 - - name: Cache Dependencies - uses: Swatinem/rust-cache@v2 + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 - - name: Check examples - run: cargo check --manifest-path=./examples/Cargo.toml + - name: Check clippy + run: cargo clippy --all-targets -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index aa0323d..f9a3c54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "actix-codec" @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.6.0", + "bitflags", "bytes", "futures-core", "futures-sink", @@ -21,23 +21,23 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" +checksum = "0fa882656b67966045e4152c634051e70346939fced7117d5f0b52146a7c74c9" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", - "ahash", - "base64 0.22.1", - "bitflags 2.6.0", + "base64", + "bitflags", "brotli", "bytes", "bytestring", - "derive_more 0.99.18", + "derive_more 2.0.1", "encoding_rs", "flate2", + "foldhash", "futures-core", "h2 0.3.26", "http 0.2.12", @@ -49,7 +49,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand", + "rand 0.9.1", "sha1", "smallvec", "tokio", @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -77,7 +77,7 @@ dependencies = [ "actix-multipart-derive", "actix-utils", "actix-web", - "derive_more 0.99.18", + "derive_more 0.99.20", "futures-core", "futures-util", "httparse", @@ -85,7 +85,7 @@ dependencies = [ "log", "memchr", "mime", - "rand", + "rand 0.8.5", "serde", "serde_json", "serde_plain", @@ -99,11 +99,11 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b" dependencies = [ - "darling 0.20.10", + "darling", "parse-size", "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "actix-server" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" +checksum = "6398974fd4284f4768af07965701efbbb5fdc0616bff20cade1bb14b77675e24" dependencies = [ "actix-rt", "actix-service", @@ -150,12 +150,11 @@ dependencies = [ [[package]] name = "actix-service" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" dependencies = [ "futures-core", - "paste", "pin-project-lite", ] @@ -171,9 +170,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.9.0" +version = "4.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" +checksum = "f2e3b15b3dc6c6ed996e4032389e9849d4ab002b1e92fbfe85b5f307d1479b4d" dependencies = [ "actix-codec", "actix-http", @@ -184,13 +183,13 @@ dependencies = [ "actix-service", "actix-utils", "actix-web-codegen", - "ahash", "bytes", "bytestring", "cfg-if", "cookie", - "derive_more 0.99.18", + "derive_more 2.0.1", "encoding_rs", + "foldhash", "futures-core", "futures-util", "impl-more", @@ -208,6 +207,7 @@ dependencies = [ "smallvec", "socket2", "time", + "tracing", "url", ] @@ -220,7 +220,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -269,7 +269,7 @@ checksum = "4c221da13534b9352f3f79fcbbd6095f6d8aee63bdf1da8a73d36f9eeea17d5a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -294,10 +294,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.16", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -324,6 +324,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -341,9 +347,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -356,50 +362,51 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "aquamarine" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e" +checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" dependencies = [ "include_dir", "itertools 0.10.5", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -408,6 +415,15 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -435,12 +451,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -449,15 +459,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "block-buffer" @@ -470,9 +474,9 @@ dependencies = [ [[package]] name = "brotli" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -481,9 +485,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -491,9 +495,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" [[package]] name = "byteorder" @@ -503,24 +513,24 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytestring" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" dependencies = [ "bytes", ] [[package]] name = "cc" -version = "1.1.30" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ "jobserver", "libc", @@ -541,23 +551,33 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] [[package]] name = "convert_case" @@ -594,13 +614,28 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -610,6 +645,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.6" @@ -622,9 +672,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -634,18 +684,18 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] [[package]] name = "ctrlc" -version = "3.4.5" +version = "3.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" dependencies = [ "nix", "windows-sys 0.59.0", @@ -653,94 +703,60 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.13.4" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.79", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", - "syn 1.0.109", + "strsim", + "syn", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core 0.20.10", + "darling_core", "quote", - "syn 2.0.79", + "syn", ] [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", + "serde", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.79", + "syn", ] [[package]] @@ -749,7 +765,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", ] [[package]] @@ -760,7 +785,19 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", + "unicode-xid", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn", "unicode-xid", ] @@ -772,6 +809,18 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -780,6 +829,12 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "dptree" version = "0.3.0" @@ -791,24 +846,27 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -829,28 +887,28 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff", "log", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erasable" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d" +checksum = "437cfb75878119ed8265685c41a115724eae43fb7cc5a0bf0e4ecc3b803af1c4" dependencies = [ "autocfg", "scopeguard", @@ -858,47 +916,86 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", ] [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "filedescriptor" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", "winapi", ] [[package]] name = "flate2" -version = "1.0.34" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -965,6 +1062,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -979,7 +1087,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -1034,13 +1142,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1061,7 +1181,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -1070,17 +1190,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap", + "http 1.3.1", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -1089,44 +1209,80 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.2", +] [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" [[package]] -name = "hermit-abi" -version = "0.4.0" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "http" -version = "0.2.12" +name = "hkdf" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "bytes", - "fnv", - "itoa", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", ] [[package]] name = "http" -version = "1.1.0" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1134,14 +1290,14 @@ dependencies = [ ] [[package]] -name = "http-body" -version = "0.4.6" +name = "http" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", - "http 0.2.12", - "pin-project-lite", + "fnv", + "itoa", ] [[package]] @@ -1151,27 +1307,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "futures-core", + "http 1.3.1", + "http-body", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1181,84 +1337,47 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "hyper" -version = "0.14.31" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", + "h2 0.4.9", + "http 1.3.1", + "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", - "socket2", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] -name = "hyper" -version = "1.5.0" +name = "hyper-rustls" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ - "bytes", - "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.5.0", - "hyper-util", - "rustls", - "rustls-pki-types", + "http 1.3.1", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.31", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -1267,7 +1386,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.0", + "hyper", "hyper-util", "native-tls", "tokio", @@ -1277,16 +1396,17 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.5.0", + "http 1.3.1", + "http-body", + "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -1296,14 +1416,15 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -1317,6 +1438,124 @@ 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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[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", + "quote", + "syn", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1325,19 +1564,30 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] name = "impl-more" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" [[package]] name = "include_dir" @@ -1360,29 +1610,41 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", + "serde", ] [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.4.0", + "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1411,25 +1673,51 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1447,15 +1735,32 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "local-channel" @@ -1486,15 +1791,25 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "md-5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] [[package]] name = "mediatype" -version = "0.19.18" +version = "0.19.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8878cd8d1b3c8c8ae4b2ba0a36652b7cf192f618a599a7fbdfa25cffd4ea72dd" +checksum = "33746aadcb41349ec291e7f2f0a3aa6834d1d7c58066fb4b01f68efc4c4b7631" [[package]] name = "memchr" @@ -1520,31 +1835,30 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -1563,7 +1877,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -1586,26 +1900,26 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -1622,20 +1936,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" dependencies = [ "cc", "libc", @@ -1643,6 +1957,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1672,12 +1992,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b" -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "percent-encoding" version = "2.3.1" @@ -1686,29 +2000,29 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1718,9 +2032,24 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] [[package]] name = "powerfmt" @@ -1730,11 +2059,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.24", ] [[package]] @@ -1748,47 +2077,59 @@ dependencies = [ ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ + "proc-macro-error-attr2", "proc-macro2", "quote", - "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +dependencies = [ + "cc", +] + [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -1796,8 +2137,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1807,7 +2158,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1816,32 +2177,41 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", ] [[package]] name = "rc-box" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0690759eabf094030c2cdabc25ade1395bac02210d920d655053c1d49583fd8" +checksum = "897fecc9fac6febd4408f9e935e86df739b0023b625e610e0357535b9c8adad0" dependencies = [ "erasable", ] [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -1851,9 +2221,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1874,20 +2244,23 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ - "base64 0.21.7", + "base64", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.31", - "hyper-tls 0.5.0", + "h2 0.4.9", + "http 1.3.1", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -1897,78 +2270,44 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", + "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tokio-util", + "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "winreg", + "windows-registry", ] [[package]] -name = "reqwest" -version = "0.12.8" +name = "rgb" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.5.0", - "hyper-rustls", - "hyper-tls 0.6.0", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 2.2.0", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "system-configuration 0.6.1", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-registry", + "bytemuck", ] [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.16", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -1990,22 +2329,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" dependencies = [ "once_cell", "rustls-pki-types", @@ -2014,15 +2353,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -2034,41 +2364,47 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "scc" -version = "2.2.2" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c1f7fc6deb21665a9060dfc7d271be784669295a31babdcd4dd2c79ae8cbfb" +checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4" dependencies = [ "sdd", ] [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -2081,9 +2417,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" -version = "3.0.4" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" +checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" [[package]] name = "security-framework" @@ -2091,7 +2427,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -2100,9 +2436,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -2110,38 +2446,38 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] name = "serde_html_form" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5" +checksum = "9d2de91cf02bbc07cde38891769ccd5d4f073d22a40683aa4bc7a95781aaa2c4" dependencies = [ "form_urlencoded", - "indexmap", + "indexmap 2.9.0", "itoa", "ryu", "serde", @@ -2149,9 +2485,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -2161,9 +2497,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ "itoa", "serde", @@ -2192,31 +2528,39 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.14.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.9.0", "serde", + "serde_derive", + "serde_json", "serde_with_macros", + "time", ] [[package]] name = "serde_with_macros" -version = "1.5.2" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "serial_test" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" +checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" dependencies = [ "futures", "log", @@ -2228,13 +2572,13 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" +checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -2248,6 +2592,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2256,9 +2611,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -2274,15 +2629,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +dependencies = [ + "serde", +] [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2293,41 +2651,203 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] -name = "strsim" -version = "0.10.0" +name = "sqlx" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-postgres", + "sqlx-sqlite", +] [[package]] -name = "strsim" -version = "0.11.1" +name = "sqlx-core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3" +dependencies = [ + "base64", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.2", + "hashlink", + "indexmap 2.9.0", + "log", + "memchr", + "native-tls", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tracing", + "url", +] [[package]] -name = "subtle" -version = "2.6.1" +name = "sqlx-macros" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] [[package]] -name = "syn" -version = "1.0.109" +name = "sqlx-macros-core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7" dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", "proc-macro2", "quote", - "unicode-ident", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "tempfile", + "tokio", + "url", ] +[[package]] +name = "sqlx-postgres" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.12", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.12", + "tracing", + "url", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" -version = "2.0.79" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -2336,28 +2856,22 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] [[package]] -name = "system-configuration" -version = "0.5.1" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys 0.5.0", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2366,19 +2880,9 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", - "system-configuration-sys 0.6.0", -] - -[[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", + "system-configuration-sys", ] [[package]] @@ -2405,13 +2909,13 @@ checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e" [[package]] name = "teloxide" -version = "0.13.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f79dd283eb21b90451c03fa7c7f83b9985130efb876b33bad89a2c208ccbc16" +checksum = "17cb7c03a9217286fe11021dc72d5a674acbb0d3b24ba38d04f7efe7920e4948" dependencies = [ "aquamarine", "bytes", - "derive_more 0.99.18", + "derive_more 1.0.0", "dptree", "either", "futures", @@ -2420,9 +2924,10 @@ dependencies = [ "pin-project", "serde", "serde_json", + "sqlx", "teloxide-core", "teloxide-macros", - "thiserror", + "thiserror 2.0.12", "tokio", "tokio-stream", "tokio-util", @@ -2431,14 +2936,14 @@ dependencies = [ [[package]] name = "teloxide-core" -version = "0.10.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1642a7ef10e7af63b8298c8d13c0f986d4fc646d42649ff060359607f62f69" +checksum = "aa2f70a3cd58c2b31ca899691b99573a40c6da713ab230bb78bbb4fb0b5c751a" dependencies = [ - "bitflags 1.3.2", + "bitflags", "bytes", "chrono", - "derive_more 0.99.18", + "derive_more 1.0.0", "either", "futures", "log", @@ -2446,13 +2951,15 @@ dependencies = [ "once_cell", "pin-project", "rc-box", - "reqwest 0.11.27", + "reqwest", + "rgb", "serde", "serde_json", "serde_with", + "stacker", "take_mut", "takecell", - "thiserror", + "thiserror 2.0.12", "tokio", "tokio-util", "url", @@ -2461,14 +2968,14 @@ dependencies = [ [[package]] name = "teloxide-macros" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2d33d809c3e7161a9ab18bedddf98821245014f0a78fa4d2c9430b2ec018c1" +checksum = "3118a980ed2ec11f73d9495a6606905bd74726e3ffe95a42fbeb187ded8fdbf4" dependencies = [ "heck", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -2481,23 +2988,23 @@ dependencies = [ "chrono", "ctrlc", "dotenv", - "env_logger 0.11.5", + "env_logger 0.11.8", "futures-util", "gag", "lazy_static", "log", "mime", "mime_guess", - "parking_lot", "pretty_env_logger", - "rand", - "reqwest 0.12.8", + "rand 0.9.1", + "reqwest", "serde", "serde_json", "serial_test", "teloxide", "teloxide_tests_macros 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio", + "tokio-util", "url", ] @@ -2507,7 +3014,7 @@ version = "0.2.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -2518,17 +3025,17 @@ checksum = "4a6bc538b4fd0adfd705c62a79db2eca5fad350347fb2e6af54358ea0c52095a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] name = "tempfile" -version = "3.13.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.2", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2545,29 +3052,49 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.12", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -2580,25 +3107,35 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" 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.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -2611,9 +3148,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -2629,13 +3166,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -2650,20 +3187,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -2672,9 +3208,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -2683,6 +3219,27 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -2691,9 +3248,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -2703,20 +3260,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -2729,30 +3286,27 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-normalization" @@ -2763,6 +3317,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -2777,9 +3337,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -2787,6 +3347,18 @@ dependencies = [ "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2795,11 +3367,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom", + "getrandom 0.3.2", ] [[package]] @@ -2829,49 +3401,65 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2879,28 +3467,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -2911,14 +3502,24 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2952,41 +3553,81 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + [[package]] name = "windows-registry" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", - "windows-strings", - "windows-targets 0.52.6", + "windows-strings 0.3.1", + "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", ] [[package]] @@ -3040,13 +3681,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3059,6 +3716,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3071,6 +3734,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3083,12 +3752,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3101,6 +3782,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3113,6 +3800,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3125,6 +3818,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3138,13 +3837,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winreg" -version = "0.50.0" +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "bitflags", +] + +[[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 = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] @@ -3153,8 +3893,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive 0.8.24", ] [[package]] @@ -3165,7 +3913,39 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] @@ -3174,29 +3954,51 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 4774750..9c680d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" [workspace.package] # MSRV -rust-version = "1.72" +rust-version = "1.80" edition = "2021" license = "MIT" diff --git a/README.md b/README.md index d08ba54..bb8bd9c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - + @@ -23,7 +23,7 @@ ## What this crate has - Easy testing of handlers with access to raw bot requests (see [hello_world_bot](https://github.com/LasterAlex/teloxide_tests/blob/master/examples/hello_world_bot/src/main.rs)) -- Support of dependencies, changes of `me` and multiple updates (see [album_bot](https://github.com/LasterAlex/teloxide_tests/blob/master/examples/album_bot/src/main.rs)) +- Support of dependencies, changes of `me`, distribution_function and multiple updates (see [album_bot](https://github.com/LasterAlex/teloxide_tests/blob/master/examples/album_bot/src/main.rs)) - Syntactic sugar and native support for storage, dialogue and states (see [calculator_bot](https://github.com/LasterAlex/teloxide_tests/blob/master/examples/calculator_bot/src/tests.rs)) - Fake file getting and downloading (see [file_download_bot](https://github.com/LasterAlex/teloxide_tests/blob/master/examples/file_download_bot/src/main.rs)) - Ability to be used with databases (see [phrase_bot](https://github.com/LasterAlex/teloxide_tests/blob/master/examples/phrase_bot/src/main.rs)) @@ -35,7 +35,7 @@ Simplified [[`hello_world_bot`]](https://github.com/LasterAlex/teloxide_tests/bl #[tokio::test] async fn test_hello_world() { let message = MockMessageText::new().text("Hi!"); - let bot = MockBot::new(message, handler_tree()); + let mut bot = MockBot::new(message, handler_tree()); // Sends the message as if it was from a user bot.dispatch().await; @@ -52,14 +52,14 @@ async fn test_hello_world() { ```rust,ignore #[tokio::test] async fn test_not_a_document() { - let bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); // Syntactic sugar bot.dispatch_and_check_last_text("Not a document").await; } #[tokio::test] async fn test_download_document_and_check() { - let bot = MockBot::new(MockMessageDocument::new(), handler_tree()); + let mut bot = MockBot::new(MockMessageDocument::new(), handler_tree()); bot.dispatch_and_check_last_text("Downloaded!").await; } ``` @@ -68,7 +68,7 @@ async fn test_download_document_and_check() { ```rust,ignore #[tokio::test] async fn test_what_is_the_first_number() { - let bot = MockBot::new(MockCallbackQuery::new().data("add"), handler_tree()); + let mut bot = MockBot::new(MockCallbackQuery::new().data("add"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::WhatDoYouWant).await; diff --git a/examples/Cargo.lock b/examples/Cargo.lock index fff70e7..1747b97 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "actix-codec" @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.6.0", + "bitflags", "bytes", "futures-core", "futures-sink", @@ -30,12 +30,12 @@ dependencies = [ "actix-service", "actix-utils", "ahash", - "base64 0.22.1", - "bitflags 2.6.0", + "base64", + "bitflags", "brotli", "bytes", "bytestring", - "derive_more 0.99.18", + "derive_more 0.99.19", "encoding_rs", "flate2", "futures-core", @@ -49,7 +49,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand", + "rand 0.8.5", "sha1", "smallvec", "tokio", @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -77,7 +77,7 @@ dependencies = [ "actix-multipart-derive", "actix-utils", "actix-web", - "derive_more 0.99.18", + "derive_more 0.99.19", "futures-core", "futures-util", "httparse", @@ -85,7 +85,7 @@ dependencies = [ "log", "memchr", "mime", - "rand", + "rand 0.8.5", "serde", "serde_json", "serde_plain", @@ -99,11 +99,11 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b" dependencies = [ - "darling 0.20.10", + "darling", "parse-size", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -189,7 +189,7 @@ dependencies = [ "bytestring", "cfg-if", "cookie", - "derive_more 0.99.18", + "derive_more 0.99.19", "encoding_rs", "futures-core", "futures-util", @@ -220,14 +220,14 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "actix-web-lab" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a965e3e826aa4737af33666aa09ed949aa1837706fda2adee07039347be50d6" +checksum = "ee75923689132fc5fb57ccc5bb98d25bb214796a29cd505844eb3b42daf11df0" dependencies = [ "actix-http", "actix-router", @@ -250,7 +250,6 @@ dependencies = [ "local-channel", "mediatype", "mime", - "once_cell", "pin-project-lite", "regex", "serde", @@ -264,20 +263,20 @@ dependencies = [ [[package]] name = "actix-web-lab-derive" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008f98f5a68eeacf5e6d44ed74ce03c1b906baa53eabfb41faf0f5f40bd685f8" +checksum = "4c221da13534b9352f3f79fcbbd6095f6d8aee63bdf1da8a73d36f9eeea17d5a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -295,10 +294,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -337,6 +336,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -354,9 +359,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -369,50 +374,51 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "aquamarine" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e" +checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" dependencies = [ "include_dir", "itertools 0.10.5", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -422,14 +428,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] -name = "async-trait" -version = "0.1.82" +name = "atoi" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", + "num-traits", ] [[package]] @@ -440,9 +444,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" @@ -459,12 +463,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -473,15 +471,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "block-buffer" @@ -505,9 +497,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -515,9 +507,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -527,15 +525,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "bytestring" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" dependencies = [ "bytes", ] @@ -556,9 +554,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "jobserver", "libc", @@ -579,23 +577,24 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "combine" @@ -611,6 +610,15 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -646,13 +654,28 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -662,6 +685,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.6" @@ -674,9 +712,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -686,9 +724,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] @@ -703,38 +741,14 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -747,19 +761,8 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.77", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", - "syn 1.0.109", + "strsim", + "syn", ] [[package]] @@ -768,18 +771,17 @@ version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.10", + "darling_core", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "deadpool" -version = "0.10.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" +checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f" dependencies = [ - "async-trait", "deadpool-runtime", "num_cpus", "tokio", @@ -787,9 +789,9 @@ dependencies = [ [[package]] name = "deadpool-redis" -version = "0.14.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f2381b0e993d06a1f6d49f486b33bc4004085bf980340fc05726bacc681fff" +checksum = "c136f185b3ca9d1f4e4e19c11570e1002f4bfdd592d589053e225716d613851f" dependencies = [ "deadpool", "redis", @@ -823,19 +825,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.77", + "syn", ] [[package]] @@ -855,17 +858,17 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", "unicode-xid", ] [[package]] name = "diesel" -version = "2.2.4" +version = "2.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e" +checksum = "04001f23ba8843dc315804fa324000376084dfb1c30794ff68dd279e6e5696d5" dependencies = [ - "bitflags 2.6.0", + "bitflags", "byteorder", "diesel_derives", "itoa", @@ -882,7 +885,7 @@ dependencies = [ "dsl_auto_type", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -891,7 +894,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ - "syn 2.0.77", + "syn", ] [[package]] @@ -902,6 +905,18 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -910,6 +925,12 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "dptree" version = "0.3.0" @@ -921,38 +942,41 @@ dependencies = [ [[package]] name = "dsl_auto_type" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +checksum = "139ae9aca7527f85f26dd76483eb38533fd84bd571065da1739656ef71c5ff5b" dependencies = [ - "darling 0.20.10", + "darling", "either", - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +dependencies = [ + "serde", +] [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -973,9 +997,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -986,15 +1010,15 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erasable" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d" +checksum = "437cfb75878119ed8265685c41a115724eae43fb7cc5a0bf0e4ecc3b803af1c4" dependencies = [ "autocfg", "scopeguard", @@ -1002,19 +1026,41 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", ] [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "file_download_bot" @@ -1028,31 +1074,48 @@ dependencies = [ [[package]] name = "filedescriptor" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", "winapi", ] [[package]] name = "flate2" -version = "1.0.33" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1079,9 +1142,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1094,9 +1157,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1104,55 +1167,66 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1194,14 +1268,26 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" @@ -1215,7 +1301,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -1224,17 +1310,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap", + "http 1.2.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -1249,15 +1335,29 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] -name = "heck" -version = "0.4.1" +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.2", +] [[package]] name = "heck" @@ -1288,21 +1388,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] -name = "http" -version = "0.2.12" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "bytes", - "fnv", - "itoa", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", ] [[package]] name = "http" -version = "1.1.0" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1310,14 +1432,14 @@ dependencies = [ ] [[package]] -name = "http-body" -version = "0.4.6" +name = "http" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", - "http 0.2.12", - "pin-project-lite", + "fnv", + "itoa", ] [[package]] @@ -1327,7 +1449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -1338,16 +1460,16 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http 1.2.0", + "http-body", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.4" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -1363,40 +1485,16 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.4.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", + "h2 0.4.8", + "http 1.2.0", + "http-body", "httparse", "itoa", "pin-project-lite", @@ -1407,13 +1505,13 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.4.1", + "http 1.2.0", + "hyper", "hyper-util", "rustls", "rustls-pki-types", @@ -1422,19 +1520,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.30", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -1443,7 +1528,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "native-tls", "tokio", @@ -1453,20 +1538,19 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.4.1", + "http 1.2.0", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -1495,26 +1579,155 @@ dependencies = [ ] [[package]] -name = "ident_case" -version = "1.0.1" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "impl-more" -version = "0.1.6" +name = "icu_locid_transform" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +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", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" [[package]] name = "include_dir" @@ -1537,29 +1750,41 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", + "serde", ] [[package]] name = "ipnet" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi 0.4.0", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1588,9 +1813,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -1603,10 +1828,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1624,15 +1850,32 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "local-channel" @@ -1663,15 +1906,25 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] [[package]] name = "mediatype" -version = "0.19.18" +version = "0.19.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8878cd8d1b3c8c8ae4b2ba0a36652b7cf192f618a599a7fbdfa25cffd4ea72dd" +checksum = "659f336e6cbe1083b58900e48649ed99fc99952a983e8d8cc992494394dd3732" [[package]] name = "memchr" @@ -1697,31 +1950,30 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -1740,18 +1992,37 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1773,26 +2044,26 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -1809,20 +2080,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1830,6 +2101,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1888,29 +2165,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1920,9 +2197,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "powerfmt" @@ -1936,15 +2213,16 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] name = "pq-sys" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cc05d7ea95200187117196eee9edd0644424911821aeb28a18ce60ea0b8793" +checksum = "30b51d65ebe1cb1f40641b15abae017fed35ccdda46e3dab1ff8768f625a3222" dependencies = [ + "libc", "vcpkg", ] @@ -1959,43 +2237,49 @@ dependencies = [ ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ + "proc-macro-error-attr2", "proc-macro2", "quote", - "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +dependencies = [ + "cc", +] + [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2007,8 +2291,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "zerocopy 0.8.21", ] [[package]] @@ -2018,7 +2313,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -2027,32 +2332,43 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.1", ] [[package]] name = "rc-box" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0690759eabf094030c2cdabc25ade1395bac02210d920d655053c1d49583fd8" +checksum = "897fecc9fac6febd4408f9e935e86df739b0023b625e610e0357535b9c8adad0" dependencies = [ "erasable", ] [[package]] name = "redis" -version = "0.24.0" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c580d9cbbe1d1b479e8d67cf9daf6a62c957e6846048408b80b43ac3f6af84cd" +checksum = "1bc42f3a12fd4408ce64d8efef67048a924e543bd35c6591c0447fda9054695f" dependencies = [ - "async-trait", + "arc-swap", "bytes", "combine", "futures-util", "itoa", + "num-bigint", "percent-encoding", "pin-project-lite", "ryu", + "socket2", "tokio", "tokio-util", "url", @@ -2060,18 +2376,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2081,9 +2397,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2098,26 +2414,29 @@ checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ - "base64 0.21.7", + "base64", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-tls 0.5.0", + "h2 0.4.8", + "http 1.2.0", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -2127,78 +2446,44 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", + "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tokio-util", + "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "winreg", + "windows-registry", ] [[package]] -name = "reqwest" -version = "0.12.7" +name = "rgb" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.4.1", - "hyper-rustls", - "hyper-tls 0.6.0", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 2.1.3", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "system-configuration 0.6.1", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-registry", + "bytemuck", ] [[package]] name = "ring" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2220,22 +2505,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "once_cell", "rustls-pki-types", @@ -2246,28 +2531,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" @@ -2280,17 +2555,23 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -2307,7 +2588,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -2316,9 +2597,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -2326,15 +2607,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -2351,23 +2632,23 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "serde_html_form" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5" +checksum = "9d2de91cf02bbc07cde38891769ccd5d4f073d22a40683aa4bc7a95781aaa2c4" dependencies = [ "form_urlencoded", - "indexmap", + "indexmap 2.7.1", "itoa", "ryu", "serde", @@ -2375,9 +2656,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -2418,24 +2699,32 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.14.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.1", "serde", + "serde_derive", + "serde_json", "serde_with_macros", + "time", ] [[package]] name = "serde_with_macros" -version = "1.5.2" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -2449,6 +2738,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2475,15 +2775,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +dependencies = [ + "serde", +] [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2494,41 +2797,201 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] -name = "strsim" -version = "0.10.0" +name = "sqlx" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-postgres", + "sqlx-sqlite", +] [[package]] -name = "strsim" -version = "0.11.1" +name = "sqlx-core" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" +dependencies = [ + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.2", + "hashlink", + "indexmap 2.7.1", + "log", + "memchr", + "native-tls", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.11", + "tokio", + "tokio-stream", + "tracing", + "url", +] [[package]] -name = "subtle" -version = "2.6.1" +name = "sqlx-macros" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] [[package]] -name = "syn" -version = "1.0.109" +name = "sqlx-macros-core" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", "proc-macro2", "quote", - "unicode-ident", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" -version = "2.0.77" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2537,28 +3000,22 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] [[package]] -name = "system-configuration" -version = "0.5.1" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys 0.5.0", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2567,19 +3024,9 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", - "system-configuration-sys 0.6.0", -] - -[[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", + "system-configuration-sys", ] [[package]] @@ -2606,14 +3053,14 @@ checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e" [[package]] name = "teloxide" -version = "0.13.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f79dd283eb21b90451c03fa7c7f83b9985130efb876b33bad89a2c208ccbc16" +checksum = "17cb7c03a9217286fe11021dc72d5a674acbb0d3b24ba38d04f7efe7920e4948" dependencies = [ "aquamarine", "bytes", "deadpool-redis", - "derive_more 0.99.18", + "derive_more 1.0.0", "dptree", "either", "futures", @@ -2623,9 +3070,10 @@ dependencies = [ "serde", "serde_cbor", "serde_json", + "sqlx", "teloxide-core", "teloxide-macros", - "thiserror", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -2634,14 +3082,14 @@ dependencies = [ [[package]] name = "teloxide-core" -version = "0.10.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1642a7ef10e7af63b8298c8d13c0f986d4fc646d42649ff060359607f62f69" +checksum = "aa2f70a3cd58c2b31ca899691b99573a40c6da713ab230bb78bbb4fb0b5c751a" dependencies = [ - "bitflags 1.3.2", + "bitflags", "bytes", "chrono", - "derive_more 0.99.18", + "derive_more 1.0.0", "either", "futures", "log", @@ -2649,13 +3097,15 @@ dependencies = [ "once_cell", "pin-project", "rc-box", - "reqwest 0.11.27", + "reqwest", + "rgb", "serde", "serde_json", "serde_with", + "stacker", "take_mut", "takecell", - "thiserror", + "thiserror 2.0.11", "tokio", "tokio-util", "url", @@ -2664,14 +3114,14 @@ dependencies = [ [[package]] name = "teloxide-macros" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2d33d809c3e7161a9ab18bedddf98821245014f0a78fa4d2c9430b2ec018c1" +checksum = "3118a980ed2ec11f73d9495a6606905bd74726e3ffe95a42fbeb187ded8fdbf4" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -2684,22 +3134,22 @@ dependencies = [ "chrono", "ctrlc", "dotenv", - "env_logger 0.11.5", + "env_logger 0.11.6", "futures-util", "gag", "lazy_static", "log", "mime", "mime_guess", - "parking_lot", "pretty_env_logger", - "rand", - "reqwest 0.12.7", + "rand 0.9.0", + "reqwest", "serde", "serde_json", "teloxide", "teloxide_tests_macros", "tokio", + "tokio-util", "url", ] @@ -2711,17 +3161,18 @@ checksum = "4a6bc538b4fd0adfd705c62a79db2eca5fad350347fb2e6af54358ea0c52095a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "tempfile" -version = "3.12.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2738,29 +3189,49 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -2779,19 +3250,29 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" 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.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -2804,9 +3285,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -2822,13 +3303,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2843,20 +3324,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -2865,9 +3345,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -2878,14 +3358,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -2905,9 +3385,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -2917,20 +3397,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -2943,30 +3423,27 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-normalization" @@ -2977,6 +3454,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -2991,9 +3474,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -3001,6 +3484,18 @@ dependencies = [ "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -3009,11 +3504,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ - "getrandom", + "getrandom 0.3.1", ] [[package]] @@ -3043,49 +3538,65 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3093,28 +3604,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -3125,14 +3639,24 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3173,6 +3697,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + [[package]] name = "windows-registry" version = "0.2.0" @@ -3352,13 +3882,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winreg" -version = "0.50.0" +name = "wit-bindgen-rt" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "bitflags", +] + +[[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 = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] @@ -3368,7 +3933,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" +dependencies = [ + "zerocopy-derive 0.8.21", ] [[package]] @@ -3379,7 +3953,39 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] @@ -3388,29 +3994,51 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.14+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5" dependencies = [ "cc", "pkg-config", diff --git a/examples/album_bot/Cargo.toml b/examples/album_bot/Cargo.toml index 897a7f3..0e304cc 100644 --- a/examples/album_bot/Cargo.toml +++ b/examples/album_bot/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -teloxide = { version = "0.13.0", features = ["macros"] } +teloxide = { version = "0.15.0", features = ["macros"] } tokio = { version = "1.38", features = ["rt-multi-thread", "macros"] } dotenv = "0.15.0" log = "0.4" diff --git a/examples/album_bot/src/main.rs b/examples/album_bot/src/main.rs index 305a0ee..ba1774a 100644 --- a/examples/album_bot/src/main.rs +++ b/examples/album_bot/src/main.rs @@ -1,15 +1,19 @@ //! This is a copy of the repo //! https://github.com/LasterAlex/AlbumTeloxideBot/blob/main/src/main.rs -use std::collections::HashMap; -use std::error::Error; -use std::sync::{Arc, Mutex}; +use std::{ + collections::HashMap, + error::Error, + sync::{Arc, Mutex}, +}; use dotenv::dotenv; -use teloxide::dispatching::UpdateHandler; -use teloxide::prelude::*; -use teloxide::types::{ - InputFile, InputMedia, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, - UpdateKind, +use teloxide::{ + dispatching::UpdateHandler, + prelude::*, + types::{ + InputFile, InputMedia, InputMediaAudio, InputMediaDocument, InputMediaPhoto, + InputMediaVideo, UpdateKind, + }, }; use tokio::time::{sleep, Duration}; @@ -34,7 +38,7 @@ async fn get_album(msg: Message, album: AlbumStorage) -> Option> { .lock() .unwrap() .entry(msg.chat.id.to_string()) - .or_insert_with(Vec::new) // If there is no entry + .or_default() // If there is no entry .push(msg.clone()); // Record length @@ -101,11 +105,10 @@ async fn main() { async fn example_handler(bot: Bot, msg: Message, album_mutex: AlbumStorage) -> HandlerResult { let album = get_album(msg.clone(), album_mutex).await; // Get either all the messages, or // None, which means that it is not the last message in the album, and we chould return - let album_messages: Vec; // Uninitialized variable, so that scoping is correct - match album { - Some(album_unwrapped) => album_messages = album_unwrapped, + let album_messages: Vec = match album { + Some(album_unwrapped) => album_unwrapped, None => return Ok(()), // If not the last message, return - } + }; // Now we have all the media group messages in the album_messages variable // And parameter msg is the last message in the album @@ -159,13 +162,18 @@ async fn example_handler(bot: Bot, msg: Message, album_mutex: AlbumStorage) -> H #[cfg(test)] mod tests { - use super::*; use teloxide::dptree::deps; use teloxide_tests::{MockBot, MockMessagePhoto, MockMessageText}; + use super::*; + #[tokio::test] async fn test_get_one_message() { - let bot = MockBot::new(MockMessagePhoto::new(), handler_tree()); + let mut bot = MockBot::new_with_distribution_function( + MockMessagePhoto::new(), + handler_tree(), + default_distribution_function, + ); let album_storage: AlbumStorage = Arc::new(Mutex::new(HashMap::new())); bot.dependencies(deps![album_storage]); @@ -175,28 +183,30 @@ mod tests { #[tokio::test] async fn test_multiple_text_messages() { - let bot = MockBot::new(vec![MockMessageText::new(); 3], handler_tree()); + let mut bot = MockBot::new_with_distribution_function( + vec![MockMessageText::new(); 3], + handler_tree(), + default_distribution_function, + ); let album_storage: AlbumStorage = Arc::new(Mutex::new(HashMap::new())); bot.dependencies(deps![album_storage]); - // ATTENTION!!! This is NOT how it would work in real life. Because we are simulating - // an update, there is no distribution function, so in reality you would see that behaviour - // only if you set the distribution function to always return None. - // I don't consider it a big deal, the default distribution function is more for real - // users, who may send multiple messages in a row, for testing no distribution function is - // fine - bot.dispatch_and_check_last_text("Detected 3 messages without media group!") + // This is 3 messages with the text "Detected 1 messages without media group!" + // They aren't bundled exactly because distribution_function only processes in parallel + // updates with a media group + bot.dispatch_and_check_last_text("Detected 1 messages without media group!") .await; - // In reality it would be 3 messages with the text "Detected 1 messages without media group!" + assert_eq!(bot.get_responses().sent_messages.len(), 3); } #[tokio::test] async fn test_get_album() { // This sends all three messages consecutively, making an album simulation, because // telegram would've sent them exactly the same way - let bot = MockBot::new( + let mut bot = MockBot::new_with_distribution_function( vec![MockMessagePhoto::new().media_group_id("123"); 3], handler_tree(), + default_distribution_function, ); let album_storage: AlbumStorage = Arc::new(Mutex::new(HashMap::new())); @@ -214,6 +224,6 @@ mod tests { Some("Detected 3 messages!") ); assert_eq!(sent_media_group.messages.len(), 3); - assert_eq!(sent_messages.len(), 3); // Just a sanity check + assert_eq!(sent_messages.len(), 3); // Just a sanity check } } diff --git a/examples/calculator_bot/Cargo.toml b/examples/calculator_bot/Cargo.toml index 1b9c05b..d908335 100644 --- a/examples/calculator_bot/Cargo.toml +++ b/examples/calculator_bot/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -teloxide = { version = "0.13.0", features = ["macros", "redis-storage", "cbor-serializer"] } +teloxide = { version = "0.15.0", features = ["macros", "redis-storage", "cbor-serializer"] } tokio = { version = "1.38", features = ["rt-multi-thread", "macros"] } dotenv = "0.15.0" serde = { version = "1.0", features = ["derive"] } diff --git a/examples/calculator_bot/src/handler_tree.rs b/examples/calculator_bot/src/handler_tree.rs index 7a3235d..970fd5a 100644 --- a/examples/calculator_bot/src/handler_tree.rs +++ b/examples/calculator_bot/src/handler_tree.rs @@ -1,18 +1,21 @@ -use crate::{get_bot_storage, handlers::*, text, MyDialogue}; -use crate::{handlers::StartCommand, State}; -use dptree::case; use std::error::Error; -use teloxide::dispatching::dialogue::GetChatId; -use teloxide::dispatching::UpdateFilterExt; -use teloxide::prelude::*; + +use dptree::case; use teloxide::{ dispatching::{ - dialogue::{self, ErasedStorage}, - UpdateHandler, + dialogue::{self, ErasedStorage, GetChatId}, + UpdateFilterExt, UpdateHandler, }, + prelude::*, types::Update, }; +use crate::{ + get_bot_storage, + handlers::{StartCommand, *}, + text, MyDialogue, State, +}; + async fn check_if_the_state_is_ok(update: Update) -> bool { // This function doesn't have anything to do with tests, but i thought i would put it here, // because i've encountered that if you update the state, and the user is on that @@ -32,7 +35,7 @@ async fn check_if_the_state_is_ok(update: Update) -> bool { .await .unwrap(); dialogue.update(State::default()).await.unwrap(); - return false; + false } } } diff --git a/examples/calculator_bot/src/handlers.rs b/examples/calculator_bot/src/handlers.rs index 304d27b..e902908 100644 --- a/examples/calculator_bot/src/handlers.rs +++ b/examples/calculator_bot/src/handlers.rs @@ -1,4 +1,3 @@ -use crate::{text, HandlerResult, MyDialogue, State}; use teloxide::{ dispatching::dialogue::GetChatId, macros::BotCommands, @@ -6,6 +5,8 @@ use teloxide::{ types::{InlineKeyboardButton, InlineKeyboardMarkup}, }; +use crate::{text, HandlerResult, MyDialogue, State}; + #[derive(BotCommands, Clone)] #[command(rename_rule = "lowercase")] pub enum StartCommand { @@ -121,4 +122,3 @@ pub async fn get_result( dialogue.update(State::default()).await?; Ok(()) } - diff --git a/examples/calculator_bot/src/main.rs b/examples/calculator_bot/src/main.rs index 09e9f41..e8f58c5 100644 --- a/examples/calculator_bot/src/main.rs +++ b/examples/calculator_bot/src/main.rs @@ -1,16 +1,17 @@ mod handler_tree; mod handlers; -pub mod text; #[cfg(test)] mod tests; +pub mod text; use std::error::Error; use dotenv::dotenv; -use teloxide::dispatching::dialogue::serializer::Cbor; -use teloxide::dispatching::dialogue::{Dialogue, ErasedStorage, RedisStorage, Storage}; -use teloxide::prelude::*; use handler_tree::handler_tree; +use teloxide::{ + dispatching::dialogue::{serializer::Cbor, Dialogue, ErasedStorage, RedisStorage, Storage}, + prelude::*, +}; pub type MyDialogue = Dialogue>; pub type HandlerResult = Result<(), Box>; @@ -36,7 +37,7 @@ pub async fn get_bot_storage() -> MyStorage { let storage: MyStorage = RedisStorage::open(&dotenv::var("REDIS_URL").unwrap(), Cbor) // For reasons unknown to me, Binary serializer doesn't accept json-like objects, // so im using it. If you want to use InMemStorage, just change - // ErasedStorage to InMemStorage (dont forget to do it in the handler_tree.rs), + // ErasedStorage to InMemStorage (dont forget to do it in the handler_tree.rs), // and make this function return InMemStorage::::new() .await .unwrap() diff --git a/examples/calculator_bot/src/tests.rs b/examples/calculator_bot/src/tests.rs index e488d81..00f4ac0 100644 --- a/examples/calculator_bot/src/tests.rs +++ b/examples/calculator_bot/src/tests.rs @@ -1,11 +1,11 @@ -use crate::{get_bot_storage, handler_tree::handler_tree, text, State}; - use teloxide::dptree::deps; use teloxide_tests::{MockBot, MockCallbackQuery, MockMessagePhoto, MockMessageText}; +use crate::{get_bot_storage, handler_tree::handler_tree, text, State}; + #[tokio::test] async fn test_start() { - let bot = MockBot::new(MockMessageText::new().text("/start"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("/start"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::Start).await; @@ -27,7 +27,7 @@ async fn test_start() { #[tokio::test] async fn test_what_is_the_first_number() { - let bot = MockBot::new(MockCallbackQuery::new().data("add"), handler_tree()); + let mut bot = MockBot::new(MockCallbackQuery::new().data("add"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::WhatDoYouWant).await; @@ -43,7 +43,7 @@ async fn test_what_is_the_first_number() { #[tokio::test] async fn test_message_errors() { - let bot = MockBot::new(MockMessageText::new().text("not a number"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("not a number"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::GetFirstNumber { @@ -63,7 +63,7 @@ async fn test_message_errors() { #[tokio::test] async fn test_what_is_the_second_number() { - let bot = MockBot::new(MockMessageText::new().text("5"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("5"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::GetFirstNumber { @@ -83,7 +83,7 @@ async fn test_what_is_the_second_number() { #[tokio::test] async fn test_add_result() { - let bot = MockBot::new(MockMessageText::new().text("4"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("4"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::GetSecondNumber { @@ -101,7 +101,7 @@ async fn test_add_result() { #[tokio::test] async fn test_subtract_result() { - let bot = MockBot::new(MockMessageText::new().text("4"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("4"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::GetSecondNumber { diff --git a/examples/calculator_bot/src/text.rs b/examples/calculator_bot/src/text.rs index 1bde8b6..3c6e993 100644 --- a/examples/calculator_bot/src/text.rs +++ b/examples/calculator_bot/src/text.rs @@ -7,4 +7,3 @@ pub const PLEASE_SEND_TEXT: &str = "Please send text, not anything else"; pub const YOUR_RESULT: &str = "Your result: "; pub const SORRY_BOT_UPDATED: &str = "Sorry, bot updated and we lost where you were. Please try again."; - diff --git a/examples/deep_linking_bot/Cargo.toml b/examples/deep_linking_bot/Cargo.toml index 5cb46f6..4741c14 100644 --- a/examples/deep_linking_bot/Cargo.toml +++ b/examples/deep_linking_bot/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -teloxide = { version = "0.13.0", features = ["macros"] } +teloxide = { version = "0.15.0", features = ["macros"] } tokio = { version = "1.38", features = ["rt-multi-thread", "macros"] } dotenv = "0.15.0" serde = { version = "1.0", features = ["derive"] } diff --git a/examples/deep_linking_bot/src/handler_tree.rs b/examples/deep_linking_bot/src/handler_tree.rs index 71c0a32..4c46e3a 100644 --- a/examples/deep_linking_bot/src/handler_tree.rs +++ b/examples/deep_linking_bot/src/handler_tree.rs @@ -1,10 +1,10 @@ -use crate::handlers::*; use dptree::case; -use teloxide::dispatching::dialogue::InMemStorage; -use teloxide::dispatching::{dialogue, UpdateFilterExt, UpdateHandler}; -use teloxide::prelude::*; +use teloxide::{ + dispatching::{dialogue, dialogue::InMemStorage, UpdateFilterExt, UpdateHandler}, + prelude::*, +}; -use crate::{StartCommand, State}; +use crate::{handlers::*, StartCommand, State}; pub fn handler_tree() -> UpdateHandler> { dialogue::enter::, State, _>() diff --git a/examples/deep_linking_bot/src/handlers.rs b/examples/deep_linking_bot/src/handlers.rs index a77b3f7..3898d86 100644 --- a/examples/deep_linking_bot/src/handlers.rs +++ b/examples/deep_linking_bot/src/handlers.rs @@ -1,6 +1,7 @@ -use crate::{add_deep_link, text, HandlerResult, MyDialogue, StartCommand, State}; use teloxide::{prelude::*, types::Me}; +use crate::{add_deep_link, text, HandlerResult, MyDialogue, StartCommand, State}; + pub async fn start( bot: Bot, msg: Message, diff --git a/examples/deep_linking_bot/src/main.rs b/examples/deep_linking_bot/src/main.rs index 194f14f..b384985 100644 --- a/examples/deep_linking_bot/src/main.rs +++ b/examples/deep_linking_bot/src/main.rs @@ -4,11 +4,11 @@ pub mod handlers; pub mod tests; pub mod text; +use std::error::Error; + use dptree::deps; use handler_tree::handler_tree; -use std::error::Error; -use teloxide::types::Me; -use teloxide::{dispatching::dialogue::InMemStorage, macros::BotCommands, prelude::*}; +use teloxide::{dispatching::dialogue::InMemStorage, macros::BotCommands, prelude::*, types::Me}; pub type MyDialogue = Dialogue>; pub type HandlerResult = Result<(), Box>; diff --git a/examples/deep_linking_bot/src/tests.rs b/examples/deep_linking_bot/src/tests.rs index c16e132..9d59dc6 100644 --- a/examples/deep_linking_bot/src/tests.rs +++ b/examples/deep_linking_bot/src/tests.rs @@ -1,15 +1,16 @@ -use crate::{add_deep_link, handler_tree::handler_tree, text, State}; use teloxide::{dispatching::dialogue::InMemStorage, dptree::deps}; use teloxide_tests::{MockBot, MockMessagePhoto, MockMessageText}; +use crate::{add_deep_link, handler_tree::handler_tree, text, State}; + #[tokio::test] async fn test_start() { // Just a regular start let mock_message = MockMessageText::new().text("/start"); - let bot = MockBot::new(mock_message.clone(), handler_tree()); + let mut bot = MockBot::new(mock_message.clone(), handler_tree()); bot.dependencies(deps![InMemStorage::::new()]); - let me = bot.me.lock().unwrap().clone(); // Yeah, we can access the default 'me' like that + let me = bot.me.clone(); // Yeah, we can access the default 'me' like that bot.dispatch_and_check_last_text_and_state( &add_deep_link(text::START, me, mock_message.chat.id), @@ -20,10 +21,10 @@ async fn test_start() { #[tokio::test] async fn test_with_deep_link() { - // Because https://t.me/some_bot?start=987654321 is the same as sending "/start 987654321", + // Because https://t.me/some_bot?start=987654321 is the same as sending "/start 987654321", // we can simulate it with this let mock_message = MockMessageText::new().text("/start 987654321"); - let bot = MockBot::new(mock_message, handler_tree()); + let mut bot = MockBot::new(mock_message, handler_tree()); bot.dependencies(deps![InMemStorage::::new()]); @@ -38,9 +39,9 @@ async fn test_with_deep_link() { async fn test_send_message() { // The text we want to send to a 987654321 user let mock_message = MockMessageText::new().text("I love you!"); - let bot = MockBot::new(mock_message.clone(), handler_tree()); + let mut bot = MockBot::new(mock_message.clone(), handler_tree()); - let me = bot.me.lock().unwrap().clone(); + let me = bot.me.clone(); bot.dependencies(deps![InMemStorage::::new()]); bot.set_state(State::WriteToSomeone { id: 987654321 }).await; @@ -57,7 +58,7 @@ async fn test_send_message() { assert_eq!( sent_message.text().unwrap(), text::YOU_HAVE_A_NEW_MESSAGE.replace("{message}", "I love you!") - ); // Just checking that the text and sender are correct + ); // Just checking that the text and sender are correct assert_eq!(sent_message.chat.id.0, 987654321); assert_eq!( @@ -70,7 +71,7 @@ async fn test_send_message() { #[tokio::test] async fn test_wrong_link() { let mock_message = MockMessageText::new().text("/start not_id"); - let bot = MockBot::new(mock_message, handler_tree()); + let mut bot = MockBot::new(mock_message, handler_tree()); bot.dependencies(deps![InMemStorage::::new()]); bot.dispatch_and_check_last_text(text::WRONG_LINK).await; @@ -79,7 +80,7 @@ async fn test_wrong_link() { #[tokio::test] async fn test_not_a_text() { let mock_message = MockMessagePhoto::new(); - let bot = MockBot::new(mock_message, handler_tree()); + let mut bot = MockBot::new(mock_message, handler_tree()); bot.dependencies(deps![InMemStorage::::new()]); bot.set_state(State::WriteToSomeone { id: 987654321 }).await; diff --git a/examples/deep_linking_bot/src/text.rs b/examples/deep_linking_bot/src/text.rs index 513da7a..b2c2ff4 100644 --- a/examples/deep_linking_bot/src/text.rs +++ b/examples/deep_linking_bot/src/text.rs @@ -1,4 +1,5 @@ -pub const START: &str = "Hello! This bot is made to ask or say something to someone completely anonymously! +pub const START: &str = + "Hello! This bot is made to ask or say something to someone completely anonymously! This link allows anyone to message you secretly: {deep_link}"; diff --git a/examples/file_download_bot/Cargo.toml b/examples/file_download_bot/Cargo.toml index 7e8a3cc..0b4de67 100644 --- a/examples/file_download_bot/Cargo.toml +++ b/examples/file_download_bot/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -teloxide = { version = "0.13.0", features = ["macros"] } +teloxide = { version = "0.15.0", features = ["macros"] } tokio = { version = "1.38", features = ["rt-multi-thread", "macros"] } dotenv = "0.15.0" diff --git a/examples/file_download_bot/src/main.rs b/examples/file_download_bot/src/main.rs index 71b9e52..eadddb4 100644 --- a/examples/file_download_bot/src/main.rs +++ b/examples/file_download_bot/src/main.rs @@ -48,18 +48,19 @@ async fn main() { #[cfg(test)] mod tests { - use super::*; use teloxide_tests::{MockBot, MockMessageDocument, MockMessageText}; + use super::*; + #[tokio::test] async fn test_not_a_document() { - let bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); bot.dispatch_and_check_last_text("Not a document").await; } #[tokio::test] async fn test_download_document_and_check() { - let bot = MockBot::new(MockMessageDocument::new(), handler_tree()); + let mut bot = MockBot::new(MockMessageDocument::new(), handler_tree()); bot.dispatch_and_check_last_text("Downloaded!").await; } } diff --git a/examples/hello_world_bot/Cargo.toml b/examples/hello_world_bot/Cargo.toml index 89d476f..ca9d6fb 100644 --- a/examples/hello_world_bot/Cargo.toml +++ b/examples/hello_world_bot/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -teloxide = { version = "0.13.0", features = ["macros"] } +teloxide = { version = "0.15.0", features = ["macros"] } tokio = { version = "1.38", features = ["rt-multi-thread", "macros"] } dotenv = "0.15.0" diff --git a/examples/hello_world_bot/src/main.rs b/examples/hello_world_bot/src/main.rs index 2a02367..8f15692 100644 --- a/examples/hello_world_bot/src/main.rs +++ b/examples/hello_world_bot/src/main.rs @@ -30,9 +30,10 @@ async fn main() { #[cfg(test)] mod tests { - use super::*; use teloxide_tests::{MockBot, MockMessageText}; + use super::*; + #[tokio::test] async fn test_hello_world() { // This is a message builder. You can check the docs for more info about mocked types @@ -40,24 +41,23 @@ mod tests { // This creates a fake bot that will send the mock_message after we dispatch it as if it was sent by the user // If you wanted, you could've made vec![MockMessageText::new().text("Hi!"), MockMessageText::new().text("Hello!")], // and both updates would've been sent one after the other. You also can make a MockMessagePhoto, MockMessageDocument, etc - let bot = MockBot::new(mock_message, handler_tree()); + let mut bot = MockBot::new(mock_message, handler_tree()); // This will dispatch the update bot.dispatch().await; // We can now check the sent messages - let responses = bot.get_responses(); // This returns a struct that has all of the recieved - // updates and requests. You can treat that function like a variable, because it basically is. + let responses = bot.get_responses(); // This returns a struct that has all of the recieved + // updates and requests. You can treat that function like a variable, because it basically is. let message = responses - .sent_messages // This is a list of all sent messages. Be warned, editing or deleting + .sent_messages // This is a list of all sent messages. Be warned, editing or deleting // messages do not affect this list! .last() .expect("No sent messages were detected!"); assert_eq!(message.text(), Some("Hello World!")); - // There is also a more specialized field, sent_messages_text: let message_text = responses - .sent_messages_text // This has a list request bodies and sent messages of only text messages, no photo, audio, etc. + .sent_messages_text // This has a list request bodies and sent messages of only text messages, no photo, audio, etc. // messages .last() .expect("No sent messages were detected!"); @@ -66,7 +66,7 @@ mod tests { // can't be accessed by looking only at the resulted message. For example, drop-down style keyboards can't // be seen in the regular message, like the parse_mode. assert_eq!(message_text.bot_request.parse_mode, None); - // Also, it is highly discouraged to use the raw bot fields like bot.updates and bot.bot, + // Also, it is highly discouraged to use the raw bot fields like bot.updates and bot.bot, // abstractions exist for a reason!!! Do not use them unless you know what you are doing! } } diff --git a/examples/phrase_bot/Cargo.toml b/examples/phrase_bot/Cargo.toml index 01b6040..353e9a8 100644 --- a/examples/phrase_bot/Cargo.toml +++ b/examples/phrase_bot/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -teloxide = { version = "0.13.0", features = ["macros", "redis-storage", "cbor-serializer"] } +teloxide = { version = "0.15.0", features = ["macros", "redis-storage", "cbor-serializer"] } tokio = { version = "1.38", features = ["rt-multi-thread", "macros"] } dotenv = "0.15.0" serde = { version = "1.0", features = ["derive"] } diff --git a/examples/phrase_bot/src/db/mod.rs b/examples/phrase_bot/src/db/mod.rs index f7870a1..88c3130 100644 --- a/examples/phrase_bot/src/db/mod.rs +++ b/examples/phrase_bot/src/db/mod.rs @@ -2,9 +2,8 @@ //! that are used in the bot. pub mod models; pub mod schema; -use models::*; - use diesel::prelude::*; +use models::*; pub fn establish_connection() -> PgConnection { dotenv::dotenv().ok(); diff --git a/examples/phrase_bot/src/db/models.rs b/examples/phrase_bot/src/db/models.rs index 47a3885..dc0e54f 100644 --- a/examples/phrase_bot/src/db/models.rs +++ b/examples/phrase_bot/src/db/models.rs @@ -1,7 +1,8 @@ -use super::schema; use diesel::prelude::*; use serde::{Deserialize, Serialize}; +use super::schema; + #[derive(Queryable, Selectable)] #[diesel(table_name = schema::users)] #[diesel(check_for_backend(diesel::pg::Pg))] diff --git a/examples/phrase_bot/src/db/schema.rs b/examples/phrase_bot/src/db/schema.rs index 299de5e..735f4ec 100644 --- a/examples/phrase_bot/src/db/schema.rs +++ b/examples/phrase_bot/src/db/schema.rs @@ -19,7 +19,4 @@ diesel::table! { diesel::joinable!(phrases -> users (user_id)); -diesel::allow_tables_to_appear_in_same_query!( - phrases, - users, -); +diesel::allow_tables_to_appear_in_same_query!(phrases, users,); diff --git a/examples/phrase_bot/src/handlers/private.rs b/examples/phrase_bot/src/handlers/private.rs index e926948..b75a07a 100644 --- a/examples/phrase_bot/src/handlers/private.rs +++ b/examples/phrase_bot/src/handlers/private.rs @@ -1,10 +1,10 @@ -use teloxide::prelude::*; -use teloxide::types::KeyboardRemove; -use teloxide::{macros::BotCommands, payloads::SendMessageSetters}; +use teloxide::{ + macros::BotCommands, payloads::SendMessageSetters, prelude::*, types::KeyboardRemove, +}; -use crate::db::models; -use crate::keyboards::menu_keyboard; -use crate::{db, keyboards, text, HandlerResult, MyDialogue, State}; +use crate::{ + db, db::models, keyboards, keyboards::menu_keyboard, text, HandlerResult, MyDialogue, State, +}; #[derive(BotCommands, Clone)] #[command(rename_rule = "lowercase")] @@ -228,16 +228,16 @@ pub async fn added_phrase( #[cfg(test)] mod tests { - use crate::{get_bot_storage, handler_tree::handler_tree}; - - use super::*; use dptree::deps; use teloxide::types::ReplyMarkup; use teloxide_tests::{MockBot, MockMessageDocument, MockMessageText, MockUser}; + use super::*; + use crate::{get_bot_storage, handler_tree::handler_tree}; + #[tokio::test] async fn test_start() { - let bot = MockBot::new(MockMessageText::new().text("/start"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("/start"), handler_tree()); // This fully deletes the user to test its creation let _ = db::delete_user(MockUser::ID as i64); @@ -262,7 +262,7 @@ mod tests { #[tokio::test] async fn test_cancel() { // Cancel is universal, so only one test is needed - let bot = MockBot::new(MockMessageText::new().text("/cancel"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("/cancel"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::ChangeNickname).await; @@ -288,7 +288,7 @@ mod tests { #[tokio::test] async fn test_profile() { - let bot = MockBot::new( + let mut bot = MockBot::new( MockMessageText::new().text(keyboards::PROFILE_BUTTON), handler_tree(), ); @@ -313,7 +313,7 @@ mod tests { #[tokio::test] async fn test_change_nickname() { - let bot = MockBot::new( + let mut bot = MockBot::new( MockMessageText::new().text(keyboards::CHANGE_NICKNAME_BUTTON), handler_tree(), ); @@ -327,7 +327,7 @@ mod tests { #[tokio::test] async fn test_changed_nickname() { - let bot = MockBot::new(MockMessageText::new().text("nickname"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("nickname"), handler_tree()); db::full_user_redeletion(MockUser::ID as i64, None); @@ -361,7 +361,7 @@ mod tests { // might race condition themselves. Because of that, write all db queries __after__ // creating the bot. Bot creation makes a lock that prevents other tests from starting, // before this one finishes - let bot = MockBot::new( + let mut bot = MockBot::new( MockMessageText::new().text(keyboards::REMOVE_PHRASE_BUTTON), handler_tree(), ); @@ -392,7 +392,7 @@ mod tests { #[tokio::test] async fn test_deleted_phrase() { - let bot = MockBot::new(MockMessageText::new().text("not a number"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("not a number"), handler_tree()); db::full_user_redeletion(MockUser::ID as i64, None); db::create_phrase( @@ -440,7 +440,7 @@ mod tests { #[tokio::test] async fn test_add_phrase() { - let bot = MockBot::new( + let mut bot = MockBot::new( MockMessageText::new().text(keyboards::ADD_PHRASE_BUTTON), handler_tree(), ); @@ -457,7 +457,7 @@ mod tests { #[tokio::test] async fn test_what_is_new_phrase_text() { - let bot = MockBot::new(MockMessageText::new().text("🤗🤗🤗🤗"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("🤗🤗🤗🤗"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::WhatIsNewPhraseEmoji).await; @@ -477,7 +477,7 @@ mod tests { #[tokio::test] async fn test_what_is_new_phrase_bot_text() { - let bot = MockBot::new(MockMessageText::new().text("hug"), handler_tree()); + let mut bot = MockBot::new(MockMessageText::new().text("hug"), handler_tree()); bot.dependencies(deps![get_bot_storage().await]); bot.set_state(State::WhatIsNewPhraseText { @@ -497,7 +497,7 @@ mod tests { #[tokio::test] async fn test_added_phrase() { - let bot = MockBot::new( + let mut bot = MockBot::new( MockMessageText::new().text("(me) hugged (reply)"), handler_tree(), ); diff --git a/examples/phrase_bot/src/handlers/public.rs b/examples/phrase_bot/src/handlers/public.rs index 5736938..51c80e2 100644 --- a/examples/phrase_bot/src/handlers/public.rs +++ b/examples/phrase_bot/src/handlers/public.rs @@ -11,7 +11,9 @@ pub async fn bot_phrase(bot: Bot, msg: Message) -> HandlerResult { let reply_from_id = reply_from.clone().id.0 as i64; let user_phrases = db::get_user_phrases(user_from_id).unwrap(); // Gets all the phrases and tries to find a matching one in the db - let phrase = user_phrases.iter().find(|phrase| phrase.text.to_lowercase() == text.to_lowercase()); + let phrase = user_phrases + .iter() + .find(|phrase| phrase.text.to_lowercase() == text.to_lowercase()); if let Some(phrase) = phrase { // If successfull, start making the test string @@ -49,9 +51,10 @@ pub async fn bot_phrase(bot: Bot, msg: Message) -> HandlerResult { #[cfg(test)] mod tests { - use crate::{db, handler_tree::handler_tree, text}; use teloxide_tests::{MockBot, MockGroupChat, MockMessageText, MockUser}; + use crate::{db, handler_tree::handler_tree, text}; + #[tokio::test] async fn test_phrase() { let chat = MockGroupChat::new().build(); @@ -67,7 +70,7 @@ mod tests { .from(MockUser::new().first_name("me").id(1234).build()) .reply_to_message(reply_message.build()); - let bot = MockBot::new(me_message, handler_tree()); + let mut bot = MockBot::new(me_message, handler_tree()); // !!! IMPORTANT !!! same as in test_delete_phrase in private handlers, do all db stuff // after creating the bot db::full_user_redeletion(1234, Some("nick1".to_string())); @@ -101,7 +104,7 @@ mod tests { .chat(chat.clone()) .from(MockUser::new().first_name("me").id(1234).build()); - let bot = MockBot::new(me_message.clone(), handler_tree()); + let mut bot = MockBot::new(me_message.clone(), handler_tree()); db::full_user_redeletion(1234, None); db::create_phrase( 1234, diff --git a/examples/phrase_bot/src/main.rs b/examples/phrase_bot/src/main.rs index 652a968..a996982 100644 --- a/examples/phrase_bot/src/main.rs +++ b/examples/phrase_bot/src/main.rs @@ -1,17 +1,17 @@ pub mod db; pub mod handlers; pub mod resources; -use db::models::Phrase; -use resources::{handler_tree, keyboards, text}; - use std::error::Error; +use db::models::Phrase; use dotenv::dotenv; use handler_tree::handler_tree; use handlers::*; -use teloxide::dispatching::dialogue::serializer::Cbor; -use teloxide::dispatching::dialogue::{Dialogue, ErasedStorage, RedisStorage, Storage}; -use teloxide::prelude::*; +use resources::{handler_tree, keyboards, text}; +use teloxide::{ + dispatching::dialogue::{serializer::Cbor, Dialogue, ErasedStorage, RedisStorage, Storage}, + prelude::*, +}; pub type MyDialogue = Dialogue>; pub type HandlerResult = Result<(), Box>; diff --git a/examples/phrase_bot/src/resources/handler_tree.rs b/examples/phrase_bot/src/resources/handler_tree.rs index 883ff55..57b390c 100644 --- a/examples/phrase_bot/src/resources/handler_tree.rs +++ b/examples/phrase_bot/src/resources/handler_tree.rs @@ -1,18 +1,22 @@ -use crate::{get_bot_storage, keyboards, private::*, public::*, text, MyDialogue}; -use crate::{private::StartCommand, State}; -use dptree::{case, entry, filter}; use std::error::Error; -use teloxide::dispatching::dialogue::GetChatId; -use teloxide::dispatching::UpdateFilterExt; -use teloxide::prelude::*; + +use dptree::{case, entry, filter}; use teloxide::{ dispatching::{ - dialogue::{self, ErasedStorage}, - UpdateHandler, + dialogue::{self, ErasedStorage, GetChatId}, + UpdateFilterExt, UpdateHandler, }, + prelude::*, types::Update, }; +use crate::{ + get_bot_storage, keyboards, + private::{StartCommand, *}, + public::*, + text, MyDialogue, State, +}; + async fn check_if_the_state_is_ok(update: Update) -> bool { // This function doesn't have anything to do with tests, but i thought i would put it here, // because i've encountered that if you update the state, and the user is on that @@ -32,7 +36,7 @@ async fn check_if_the_state_is_ok(update: Update) -> bool { .await .unwrap(); dialogue.update(State::default()).await.unwrap(); - return false; + false } } } diff --git a/examples/phrase_bot/src/resources/text.rs b/examples/phrase_bot/src/resources/text.rs index d17b595..b7d1ad1 100644 --- a/examples/phrase_bot/src/resources/text.rs +++ b/examples/phrase_bot/src/resources/text.rs @@ -25,7 +25,7 @@ If you want to return, send /cancel"; pub const CANCELED: &str = "Canceled."; -pub fn delete_phrase(all_phrases: &Vec) -> String { +pub fn delete_phrase(all_phrases: &[models::Phrase]) -> String { format!( "These are your phrases: @@ -97,17 +97,17 @@ pub fn make_phrase_string(phrase: &models::Phrase) -> String { format!("{} - {} | {}", phrase.text, phrase.emoji, phrase.bot_text) } -pub fn list_all_phrases(phrases: &Vec) -> String { +pub fn list_all_phrases(phrases: &[models::Phrase]) -> String { phrases .iter() - .map(|phrase| make_phrase_string(phrase)) + .map(make_phrase_string) .enumerate() .map(|(i, phrase)| format!("{}. {}", i + 1, phrase)) .collect::>() .join("\n\n") } -pub fn profile(nickname: Option, phrases: &Vec) -> String { +pub fn profile(nickname: Option, phrases: &[models::Phrase]) -> String { format!( "Your nickname📜: {} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..3f5d680 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2024-10-10" +components = [ "rustfmt", "clippy" ] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..455c820 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +imports_granularity = "Crate" +group_imports = "StdExternalCrate" diff --git a/teloxide_tests/Cargo.toml b/teloxide_tests/Cargo.toml index 471a075..ec6c9f9 100644 --- a/teloxide_tests/Cargo.toml +++ b/teloxide_tests/Cargo.toml @@ -20,7 +20,7 @@ log = "0.4" pretty_env_logger = "0.5" url = "2.5.1" reqwest = "0.12.5" -teloxide = { version = "0.13.0", features = ["macros"] } +teloxide = { version = "0.15.0", features = ["macros", "sqlite-storage-nativetls"] } tokio = { version = "1.38", features = ["rt-multi-thread", "macros"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -28,14 +28,14 @@ teloxide_tests_macros = "0.2.0" mime = "0.3.17" chrono = "0.4.38" actix-web-lab = "0.23.0" -parking_lot = "0.12.3" mime_guess = "2.0.5" -rand = "0.8.5" +rand = "0.9.0" actix-multipart = "0.7.2" lazy_static = "1.5.0" futures-util = "0.3" actix-web = "4.9" env_logger = "0.11.5" +tokio-util = "0.7.12" [dev-dependencies] serial_test = { version = "3.1.1" } diff --git a/teloxide_tests/src/dataset/chat.rs b/teloxide_tests/src/dataset/chat.rs index 046f665..dda1757 100644 --- a/teloxide_tests/src/dataset/chat.rs +++ b/teloxide_tests/src/dataset/chat.rs @@ -1,11 +1,7 @@ -use crate::proc_macros::Changeable; use teloxide::types::*; -use teloxide::types::{ - Chat, ChatId, ChatKind, ChatLocation, ChatPermissions, ChatPhoto, ChatPrivate, ChatPublic, - Message, PublicChatChannel, PublicChatGroup, PublicChatKind, PublicChatSupergroup, True, -}; -use super::{MockChatFullInfo, MockUser}; +use super::MockUser; +use crate::proc_macros::Changeable; macro_rules! Chat { ( @@ -17,30 +13,14 @@ macro_rules! Chat { #[derive($($derive),*)] $pub struct $name { // This is basically a template pub id: ChatId, - pub photo: Option, - pub available_reactions: Option>, - pub pinned_message: Option>, - pub message_auto_delete_time: Option, - pub has_hidden_members: bool, - pub has_aggressive_anti_spam_enabled: bool, - pub chat_full_info: ChatFullInfo, $($fpub $field : $type,)* } impl $name { pub const ID: i64 = -12345678; // Make them into a constant cuz why not - pub const HAS_HIDDEN_MEMBERS: bool = false; - pub const AGGRESSIVE_ANTI_SPAM_ENABLED: bool = false; - pub(crate) fn new_chat($($field:$type,)*) -> Self{ + pub(crate) fn new_chat($($field:$type,)*) -> Self { Self { // To not repeat this over and over again id: ChatId(Self::ID), - photo: None, - available_reactions: None, - pinned_message: None, - message_auto_delete_time: None, - has_hidden_members: Self::HAS_HIDDEN_MEMBERS, - has_aggressive_anti_spam_enabled: Self::AGGRESSIVE_ANTI_SPAM_ENABLED, - chat_full_info: MockChatFullInfo::new().build(), $($field,)* } } @@ -49,20 +29,13 @@ macro_rules! Chat { Chat { id: self.id, kind: chat_kind, - available_reactions: self.available_reactions, - photo: self.photo, - pinned_message: self.pinned_message, - message_auto_delete_time: self.message_auto_delete_time, - has_hidden_members: self.has_hidden_members, - has_aggressive_anti_spam_enabled: self.has_aggressive_anti_spam_enabled, - chat_full_info: self.chat_full_info, } } } } } -macro_rules! PublicChat { // A specialization of Chat!, again, to not repeat myself +macro_rules! ChatPublic { // A specialization of Chat!, again, to not repeat myself ( #[derive($($derive:meta),*)] $pub:vis struct $name:ident { @@ -73,41 +46,30 @@ macro_rules! PublicChat { // A specialization of Chat!, again, to not repeat my #[derive($($derive),*)] $pub struct $name { pub title: Option, - pub description: Option, - pub invite_link: Option, - pub has_protected_content: Option, $($fpub $field : $type,)* } } impl $name { - pub(crate) fn new_public_chat($($field:$type,)*) -> Self { + pub(crate) fn new_chat_public($($field:$type,)*) -> Self { $name::new_chat( - None, - None, - None, None, $($field,)* ) } - pub(crate) fn build_public_chat(self, public_chat_kind: PublicChatKind) -> Chat { + pub(crate) fn build_chat_public(self, chat_public_kind: PublicChatKind) -> Chat { self.clone().build_chat(ChatKind::Public(ChatPublic { title: self.title, - kind: public_chat_kind, - description: self.description, - invite_link: self.invite_link, - has_protected_content: self.has_protected_content, + kind: chat_public_kind, })) } } } } -PublicChat! { +ChatPublic! { #[derive(Changeable, Clone)] - pub struct MockGroupChat { - pub permissions: Option, - } + pub struct MockGroupChat { } } impl MockGroupChat { @@ -122,7 +84,7 @@ impl MockGroupChat { /// ``` /// pub fn new() -> Self { - Self::new_public_chat(None) + Self::new_chat_public() } /// Builds the group chat @@ -135,23 +97,19 @@ impl MockGroupChat { /// ``` /// pub fn build(self) -> Chat { - self.clone() - .build_public_chat(PublicChatKind::Group(PublicChatGroup { - permissions: self.permissions, - })) + self.clone().build_chat_public(PublicChatKind::Group) } } -PublicChat! { +ChatPublic! { #[derive(Changeable, Clone)] pub struct MockChannelChat { - pub linked_chat_id: Option, pub username: Option, } } impl MockChannelChat { - /// Creates a new easily changable channel chat builder + /// Creates a new easily changable channel builder /// /// Example: /// ``` @@ -164,7 +122,7 @@ impl MockChannelChat { /// ``` /// pub fn new() -> Self { - Self::new_public_chat(None, None) + Self::new_chat_public(None) } /// Builds the channel chat @@ -179,34 +137,24 @@ impl MockChannelChat { /// pub fn build(self) -> Chat { self.clone() - .build_public_chat(PublicChatKind::Channel(PublicChatChannel { - linked_chat_id: self.linked_chat_id, + .build_chat_public(PublicChatKind::Channel(PublicChatChannel { username: self.username, })) } } -PublicChat! { +ChatPublic! { #[derive(Changeable, Clone)] pub struct MockSupergroupChat { pub username: Option, - pub active_usernames: Option>, pub is_forum: bool, - pub sticker_set_name: Option, - pub can_set_sticker_set: Option, - pub permissions: Option, - pub slow_mode_delay: Option, - pub linked_chat_id: Option, - pub location: Option, - pub join_to_send_messages: Option, - pub join_by_request: Option, } } impl MockSupergroupChat { pub const IS_FORUM: bool = false; - /// Creates a new easily changable supergroup chat builder + /// Creates a new easily changable supergroup chat full info builder /// /// Example: /// ``` @@ -217,19 +165,7 @@ impl MockSupergroupChat { /// ``` /// pub fn new() -> Self { - Self::new_public_chat( - None, - None, - Self::IS_FORUM, - None, - None, - None, - None, - None, - None, - None, - None, - ) + Self::new_chat_public(None, Self::IS_FORUM) } /// Builds the supergroup chat @@ -243,18 +179,9 @@ impl MockSupergroupChat { /// pub fn build(self) -> Chat { self.clone() - .build_public_chat(PublicChatKind::Supergroup(PublicChatSupergroup { + .build_chat_public(PublicChatKind::Supergroup(PublicChatSupergroup { username: self.username, - active_usernames: self.active_usernames, is_forum: self.is_forum, - sticker_set_name: self.sticker_set_name, - can_set_sticker_set: self.can_set_sticker_set, - permissions: self.permissions, - slow_mode_delay: self.slow_mode_delay, - linked_chat_id: self.linked_chat_id, - location: self.location, - join_to_send_messages: self.join_to_send_messages, - join_by_request: self.join_by_request, })) } } @@ -265,9 +192,6 @@ Chat! { pub username: Option, pub first_name: Option, pub last_name: Option, - pub bio: Option, - pub has_private_forwards: Option, - pub has_restricted_voice_and_video_messages: Option, } } @@ -283,7 +207,7 @@ impl MockPrivateChat { /// ``` /// pub fn new() -> Self { - Self::new_chat(None, None, None, None, None, None).id(MockUser::ID as i64) + Self::new_chat(None, None, None).id(MockUser::ID as i64) } /// Builds the private chat @@ -300,9 +224,6 @@ impl MockPrivateChat { username: self.username, first_name: self.first_name, last_name: self.last_name, - bio: self.bio, - has_private_forwards: self.has_private_forwards, - has_restricted_voice_and_video_messages: self.has_restricted_voice_and_video_messages, })) } } diff --git a/teloxide_tests/src/dataset/chat_full_info.rs b/teloxide_tests/src/dataset/chat_full_info.rs new file mode 100644 index 0000000..f739f17 --- /dev/null +++ b/teloxide_tests/src/dataset/chat_full_info.rs @@ -0,0 +1,371 @@ +use chrono::{DateTime, Utc}; +use teloxide::types::*; + +use super::MockUser; +use crate::proc_macros::Changeable; + +macro_rules! ChatFullInfo { + ( + #[derive($($derive:meta),*)] + $pub:vis struct $name:ident { + $($fpub:vis $field:ident : $type:ty,)* + } + ) => { + #[derive($($derive),*)] + $pub struct $name { // This is basically a template + pub id: ChatId, + pub photo: Option, + pub pinned_message: Option>, + pub message_auto_delete_time: Option, + pub has_hidden_members: bool, + pub has_aggressive_anti_spam_enabled: bool, + pub accent_color_id: Option, + pub background_custom_emoji_id: Option, + pub profile_accent_color_id: Option, + pub profile_background_custom_emoji_id: Option, + pub emoji_status_custom_emoji_id: Option, + pub emoji_status_expiration_date: Option>, + pub has_visible_history: bool, + pub max_reaction_count: u8, + $($fpub $field : $type,)* + } + impl $name { + pub const ID: i64 = -12345678; // Make them into a constant cuz why not + pub const HAS_HIDDEN_MEMBERS: bool = false; + pub const AGGRESSIVE_ANTI_SPAM_ENABLED: bool = false; + pub const HAS_VISIBLE_HISTORY: bool = true; + pub const MAX_REACTION_COUNT: u8 = 100; + + pub(crate) fn new_chat_full_info($($field:$type,)*) -> Self{ + Self { // To not repeat this over and over again + id: ChatId(Self::ID), + photo: None, + pinned_message: None, + message_auto_delete_time: None, + has_hidden_members: Self::HAS_HIDDEN_MEMBERS, + has_aggressive_anti_spam_enabled: Self::AGGRESSIVE_ANTI_SPAM_ENABLED, + accent_color_id: None, + background_custom_emoji_id: None, + profile_accent_color_id: None, + profile_background_custom_emoji_id: None, + emoji_status_custom_emoji_id: None, + emoji_status_expiration_date: None, + has_visible_history: Self::HAS_VISIBLE_HISTORY, + max_reaction_count: Self::MAX_REACTION_COUNT, + $($field,)* + } + } + + pub(crate) fn build_chat_full_info(self, chat_full_info_kind: ChatFullInfoKind) -> ChatFullInfo { + ChatFullInfo { + id: self.id, + photo: self.photo, + pinned_message: self.pinned_message, + message_auto_delete_time: self.message_auto_delete_time, + has_hidden_members: self.has_hidden_members, + has_aggressive_anti_spam_enabled: self.has_aggressive_anti_spam_enabled, + accent_color_id: self.accent_color_id, + background_custom_emoji_id: self.background_custom_emoji_id, + profile_accent_color_id: self.profile_accent_color_id, + profile_background_custom_emoji_id: self.profile_background_custom_emoji_id, + emoji_status_custom_emoji_id: self.emoji_status_custom_emoji_id, + emoji_status_expiration_date: self.emoji_status_expiration_date, + has_visible_history: self.has_visible_history, + max_reaction_count: self.max_reaction_count, + kind: chat_full_info_kind, + } + } + } + } +} + +macro_rules! ChatFullInfoPublic { // A specialization of Chat!, again, to not repeat myself + ( + #[derive($($derive:meta),*)] + $pub:vis struct $name:ident { + $($fpub:vis $field:ident : $type:ty,)* + } + ) => { + ChatFullInfo! { + #[derive($($derive),*)] + $pub struct $name { + pub title: Option, + pub description: Option, + pub invite_link: Option, + pub has_protected_content: bool, + pub available_reactions: Option>, + $($fpub $field : $type,)* + } + } + impl $name { + pub const HAS_PROTECTED_CONTENT: bool = false; + + pub(crate) fn new_chat_full_info_public($($field:$type,)*) -> Self { + $name::new_chat_full_info( + None, + None, + None, + Self::HAS_PROTECTED_CONTENT, + None, + $($field,)* + ) + } + + pub(crate) fn build_chat_full_info_public(self, chat_full_info_public_kind: ChatFullInfoPublicKind) -> ChatFullInfo { + self.clone().build_chat_full_info(ChatFullInfoKind::Public(Box::new(ChatFullInfoPublic { + title: self.title, + kind: chat_full_info_public_kind, + description: self.description, + invite_link: self.invite_link, + has_protected_content: self.has_protected_content, + available_reactions: self.available_reactions, + }))) + } + } + } +} + +ChatFullInfoPublic! { + #[derive(Changeable, Clone)] + pub struct MockChatFullInfoGroup { + pub permissions: Option, + } +} + +impl MockChatFullInfoGroup { + /// Creates a new easily changable group chat full info builder + /// + /// Example: + /// ``` + /// let chat = teloxide_tests::MockChatFullInfoGroup::new() + /// .id(-1234) + /// .build(); + /// assert_eq!(chat.id.0, -1234); + /// ``` + /// + pub fn new() -> Self { + Self::new_chat_full_info_public(None) + } + + /// Builds the group chat full info + /// + /// Example: + /// ``` + /// let mock_chat = teloxide_tests::MockChatFullInfoGroup::new(); + /// let chat = mock_chat.build(); + /// assert_eq!(chat.id.0, teloxide_tests::MockChatFullInfoGroup::ID); // ID is a default value + /// ``` + /// + pub fn build(self) -> ChatFullInfo { + self.clone() + .build_chat_full_info_public(ChatFullInfoPublicKind::Group(ChatFullInfoPublicGroup { + permissions: self.permissions, + })) + } +} + +ChatFullInfoPublic! { + #[derive(Changeable, Clone)] + pub struct MockChatFullInfoChannel { + pub username: Option, + pub linked_chat_id: Option, + } +} + +impl MockChatFullInfoChannel { + /// Creates a new easily changable channel chat full info builder + /// + /// Example: + /// ``` + /// let chat = teloxide_tests::MockChatFullInfoChannel::new() + /// .id(-1234) + /// .username("test_channel") + /// .build(); + /// assert_eq!(chat.id.0, -1234); + /// assert_eq!(chat.username(), Some("test_channel")); + /// ``` + /// + pub fn new() -> Self { + Self::new_chat_full_info_public(None, None) + } + + /// Builds the channel chat full info + /// + /// Example: + /// ``` + /// let mock_chat = teloxide_tests::MockChatFullInfoChannel::new(); + /// let chat = mock_chat.build(); + /// assert_eq!(chat.id.0, teloxide_tests::MockChatFullInfoChannel::ID); // ID is a default value + /// assert_eq!(chat.username(), None); + /// ``` + /// + pub fn build(self) -> ChatFullInfo { + self.clone() + .build_chat_full_info_public(ChatFullInfoPublicKind::Channel( + ChatFullInfoPublicChannel { + username: self.username, + linked_chat_id: self.linked_chat_id, + }, + )) + } +} + +ChatFullInfoPublic! { + #[derive(Changeable, Clone)] + pub struct MockChatFullInfoSupergroup { + pub username: Option, + pub active_usernames: Option>, + pub is_forum: bool, + pub sticker_set_name: Option, + pub can_set_sticker_set: bool, + pub custom_emoji_sticker_set_name: Option, + pub permissions: Option, + pub slow_mode_delay: Option, + pub unrestrict_boost_count: Option, + pub linked_chat_id: Option, + pub location: Option, + pub join_to_send_messages: bool, + pub join_by_request: bool, + } +} + +impl MockChatFullInfoSupergroup { + pub const IS_FORUM: bool = false; + pub const CAN_SET_STICKER_SET: bool = false; + pub const JOIN_TO_SEND_MESSAGES: bool = false; + pub const JOIN_BY_REQUEST: bool = false; + + /// Creates a new easily changable supergroup chat full info builder + /// + /// Example: + /// ``` + /// let chat = teloxide_tests::MockChatFullInfoSupergroup::new() + /// .id(-1234) + /// .build(); + /// assert_eq!(chat.id.0, -1234); + /// ``` + /// + pub fn new() -> Self { + Self::new_chat_full_info_public( + None, + None, + Self::IS_FORUM, + None, + Self::CAN_SET_STICKER_SET, + None, + None, + None, + None, + None, + None, + Self::JOIN_TO_SEND_MESSAGES, + Self::JOIN_BY_REQUEST, + ) + } + + /// Builds the supergroup chat + /// + /// Example: + /// ``` + /// let mock_chat = teloxide_tests::MockChatFullInfoSupergroup::new(); + /// let chat = mock_chat.build(); + /// assert_eq!(chat.id.0, teloxide_tests::MockChatFullInfoSupergroup::ID); // ID is a default value + /// ``` + /// + pub fn build(self) -> ChatFullInfo { + self.clone() + .build_chat_full_info_public(ChatFullInfoPublicKind::Supergroup( + ChatFullInfoPublicSupergroup { + username: self.username, + active_usernames: self.active_usernames, + is_forum: self.is_forum, + sticker_set_name: self.sticker_set_name, + can_set_sticker_set: self.can_set_sticker_set, + permissions: self.permissions, + slow_mode_delay: self.slow_mode_delay, + linked_chat_id: self.linked_chat_id, + location: self.location, + join_to_send_messages: self.join_to_send_messages, + join_by_request: self.join_by_request, + custom_emoji_sticker_set_name: self.custom_emoji_sticker_set_name, + unrestrict_boost_count: self.unrestrict_boost_count, + }, + )) + } +} + +ChatFullInfo! { + #[derive(Changeable, Clone)] + pub struct MockChatFullInfoPrivate { + pub username: Option, + pub first_name: Option, + pub last_name: Option, + pub bio: Option, + pub has_private_forwards: bool, + pub has_restricted_voice_and_video_messages: bool, + pub personal_chat: Option>, + pub birthdate: Option, + pub business_intro: Option, + pub business_location: Option, + pub business_opening_hours: Option, + } +} + +impl MockChatFullInfoPrivate { + pub const HAS_PRIVATE_FORWARDS: bool = false; + pub const HAS_RESTRICTED_VOICE_AND_VIDEO_MESSAGES: bool = false; + + /// Creates a new easily changable private chat full info builder + /// + /// Example: + /// ``` + /// let chat = teloxide_tests::MockChatFullInfoPrivate::new() + /// .id(-1234) + /// .build(); + /// assert_eq!(chat.id.0, -1234); + /// ``` + /// + pub fn new() -> Self { + Self::new_chat_full_info( + None, + None, + None, + None, + Self::HAS_PRIVATE_FORWARDS, + Self::HAS_RESTRICTED_VOICE_AND_VIDEO_MESSAGES, + None, + None, + None, + None, + None, + ) + .id(MockUser::ID as i64) + } + + /// Builds the private chat full info + /// + /// Example: + /// ``` + /// let mock_chat = teloxide_tests::MockChatFullInfoPrivate::new(); + /// let chat = mock_chat.build(); + /// assert_eq!(chat.id.0 as u64, teloxide_tests::MockUser::ID); // Private chats have the id of users + /// ``` + /// + pub fn build(self) -> ChatFullInfo { + self.clone() + .build_chat_full_info(ChatFullInfoKind::Private(Box::new(ChatFullInfoPrivate { + username: self.username, + first_name: self.first_name, + last_name: self.last_name, + bio: self.bio, + has_private_forwards: self.has_private_forwards, + has_restricted_voice_and_video_messages: self + .has_restricted_voice_and_video_messages, + birthdate: self.birthdate, + business_intro: self.business_intro, + business_location: self.business_location, + business_opening_hours: self.business_opening_hours, + personal_chat: self.personal_chat, + }))) + } +} diff --git a/teloxide_tests/src/dataset/message.rs b/teloxide_tests/src/dataset/message.rs index 2ed0660..2785ac7 100644 --- a/teloxide_tests/src/dataset/message.rs +++ b/teloxide_tests/src/dataset/message.rs @@ -1,9 +1,10 @@ -use super::chat::MockPrivateChat; -use crate::proc_macros::Changeable; -use chrono::{DateTime, Utc}; use core::sync::atomic::{AtomicI32, Ordering}; + +use chrono::{DateTime, Utc}; use teloxide::types::*; -use crate::MockUser; + +use super::chat::MockPrivateChat; +use crate::{proc_macros::Changeable, MockUser}; macro_rules! Message { ( @@ -22,6 +23,7 @@ macro_rules! Message { pub chat: Chat, pub is_topic_message: bool, pub via_bot: Option, + pub sender_business_bot: Option, $($fpub $field : $type,)* } impl $name { @@ -36,6 +38,7 @@ macro_rules! Message { chat: MockPrivateChat::new().build(), is_topic_message: false, via_bot: None, + sender_business_bot: None, $($field,)* } } @@ -51,25 +54,28 @@ macro_rules! Message { is_topic_message: self.is_topic_message, via_bot: self.via_bot, kind: message_kind, + sender_business_bot: self.sender_business_bot, } } } impl crate::dataset::IntoUpdate for $name { - /// Converts the MockCallbackQuery into an updates vector + /// Converts the mock message into an updates vector /// /// # Example /// ``` /// use teloxide_tests::IntoUpdate; + /// use teloxide::types::{UpdateId, UpdateKind::Message}; + /// use std::sync::atomic::AtomicI32; + /// /// let mock_message = teloxide_tests::MockMessageText::new(); - /// let update = mock_message.clone().into_update(1.into())[0].clone(); - /// assert_eq!(update.id, teloxide::types::UpdateId(1)); - /// assert_eq!(update.kind, teloxide::types::UpdateKind::Message( - /// mock_message.build()) - /// ); + /// let update = mock_message.clone().into_update(&AtomicI32::new(42))[0].clone(); + /// + /// assert_eq!(update.id, UpdateId(42)); + /// assert_eq!(update.kind, Message(mock_message.build())); /// ``` /// - fn into_update(self, id: AtomicI32) -> Vec { + fn into_update(self, id: &AtomicI32) -> Vec { vec![Update { id: UpdateId(id.fetch_add(1, Ordering::Relaxed) as u32), kind: UpdateKind::Message(self.build()), @@ -81,6 +87,59 @@ macro_rules! Message { pub(crate) use Message; +#[derive(Clone, Debug, PartialEq)] +pub struct MockEditedMessage(Message); + +impl MockEditedMessage { + /// Creates a new MockEditedMessage wrapper. + /// + /// This is useful for testing the `UpdateKind::EditedMessage` variant. + /// + /// # Example + /// ``` + /// use chrono::Utc; + /// + /// let message = teloxide_tests::MockMessageText::new().edit_date(Utc::now()).build(); + /// let edited_message = teloxide_tests::MockEditedMessage::new(message.clone()); + /// assert_eq!(edited_message.message(), &message); + pub fn new(mut message: Message) -> Self { + if let MessageKind::Common(ref mut common) = message.kind { + common.edit_date = common.edit_date.or(Some(Utc::now())); + } + Self(message) + } + + pub fn message(&self) -> &Message { + &self.0 + } +} + +impl crate::dataset::IntoUpdate for MockEditedMessage { + /// Converts the edited Message into an updates vector + /// + /// # Example + /// ``` + /// use chrono::Utc; + /// use teloxide_tests::IntoUpdate; + /// use teloxide::types::{UpdateId, UpdateKind}; + /// use std::sync::atomic::AtomicI32; + /// + /// let message = teloxide_tests::MockMessageText::new().edit_date(Utc::now()).build(); + /// let edited_message = teloxide_tests::MockEditedMessage::new(message.clone()); + /// let update = edited_message.into_update(&AtomicI32::new(42))[0].clone(); + /// + /// assert_eq!(update.id, UpdateId(42)); + /// assert_eq!(update.kind, UpdateKind::EditedMessage(message)); + /// ``` + /// + fn into_update(self, id: &AtomicI32) -> Vec { + vec![Update { + id: UpdateId(id.fetch_add(1, Ordering::Relaxed) as u32), + kind: UpdateKind::EditedMessage(self.0), + }] + } +} + // More messages like Webapp data is needed Message! { @@ -127,3 +186,101 @@ impl MockMessageDice { })) } } + +Message! { + #[derive(Changeable, Clone)] + pub struct MockMessageInvoice { + pub title: String, + pub description: String, + pub start_parameter: String, + pub currency: String, + pub total_amount: u32, + } +} + +impl MockMessageInvoice { + pub const TITLE: &'static str = "Title of Invoice"; + pub const DESCRIPTION: &'static str = "Description of Invoice"; + pub const START_PARAMETER: &'static str = "Start parameter of Invoice"; + pub const CURRENCY: &'static str = "XTR"; + pub const TOTAL_AMOUNT: u32 = 0; + + /// Creates a new easily changable message invoice builder + /// + /// # Example + /// ``` + /// let message = teloxide_tests::MockMessageInvoice::new() + /// .title("Some title") + /// .build(); + /// assert_eq!(message.invoice().unwrap().title, "Some title".to_owned()); + /// ``` + /// + pub fn new() -> Self { + Self::new_message( + Self::TITLE.to_owned(), + Self::DESCRIPTION.to_owned(), + Self::START_PARAMETER.to_owned(), + Self::CURRENCY.to_owned(), + Self::TOTAL_AMOUNT, + ) + } + + /// Builds the message dice + /// + /// # Example + /// ``` + /// let mock_message = teloxide_tests::MockMessageInvoice::new(); + /// let message = mock_message.build(); + /// assert_eq!(message.invoice().unwrap().currency, teloxide_tests::MockMessageInvoice::CURRENCY); // CURRENCY is a default value + /// ``` + /// + pub fn build(self) -> Message { + self.clone() + .build_message(MessageKind::Invoice(MessageInvoice { + invoice: Invoice { + title: self.title, + description: self.description, + start_parameter: self.start_parameter, + currency: self.currency, + total_amount: self.total_amount, + }, + })) + } +} + +Message! { + #[derive(Changeable, Clone)] + pub struct MockMessageNewChatMembers { + pub new_chat_members: Vec, + } +} + +impl MockMessageNewChatMembers { + /// Creates a new easily changeable new chat member message builder + /// + /// # Example + /// ``` + /// let message = teloxide_tests::MockMessageNewChatMembers::new() + /// .new_chat_members(vec![teloxide_tests::MockUser::new().id(123).build()]) + /// .build(); + /// assert_eq!(message.new_chat_members().unwrap()[0].id.0, 123); + pub fn new() -> Self { + Self::new_message(vec![MockUser::new().build()]) + } + + /// Builds the new chat member message + /// + /// # Example + /// ``` + /// let mock_message = teloxide_tests::MockMessageNewChatMembers::new(); + /// let message = mock_message.build(); + /// assert_eq!(message.new_chat_members().unwrap().len(), 1); // Contains a single MockUser by default + /// ``` + /// + pub fn build(self) -> Message { + self.clone() + .build_message(MessageKind::NewChatMembers(MessageNewChatMembers { + new_chat_members: self.new_chat_members, + })) + } +} diff --git a/teloxide_tests/src/dataset/message_common.rs b/teloxide_tests/src/dataset/message_common.rs index 8fe2982..8316359 100644 --- a/teloxide_tests/src/dataset/message_common.rs +++ b/teloxide_tests/src/dataset/message_common.rs @@ -1,13 +1,15 @@ -use super::message::Message; -use super::{chat::MockPrivateChat, MockUser}; -use super::{MockLocation, MockPhotoSize, MockVideo}; -use crate::proc_macros::Changeable; -use chrono::{DateTime, Utc}; use core::sync::atomic::{AtomicI32, Ordering}; + +use chrono::{DateTime, Utc}; use mime::Mime; use teloxide::types::*; -macro_rules! MessageCommon { // Rust was supposed to be used withot inheritance, and yet here i am, reinventing it... +use super::{ + chat::MockPrivateChat, message::Message, MockLocation, MockPhotoSize, MockUser, MockVideo, +}; +use crate::proc_macros::Changeable; + +macro_rules! MessageCommon { // Rust was supposed to be used without inheritance, and yet here i am, reinventing it... ( #[derive($($derive:meta),*)] $pub:vis struct $name:ident { @@ -18,6 +20,7 @@ macro_rules! MessageCommon { // Rust was supposed to be used withot inheritance #[derive($($derive),*)] $pub struct $name { pub author_signature: Option, + pub effect_id: Option, pub forward_origin: Option, pub reply_to_message: Option>, pub external_reply: Option, @@ -26,12 +29,18 @@ macro_rules! MessageCommon { // Rust was supposed to be used withot inheritance pub reply_markup: Option, pub is_automatic_forward: bool, pub has_protected_content: bool, + pub is_from_offline: bool, + pub business_connection_id: Option, + pub reply_to_story: Option, + pub sender_boost_count: Option, + $($fpub $field : $type,)* // Just all of the other fields, nothig too scary here } } impl $name { // Implements common functions pub const IS_AUTOMATIC_FORWARD: bool = false; pub const HAS_PROTECTED_CONTENT: bool = false; + pub const IS_FROM_OFFLINE: bool = false; pub(crate) fn new_message_common($($field:$type,)*) -> Self { $name::new_message( @@ -42,8 +51,13 @@ macro_rules! MessageCommon { // Rust was supposed to be used withot inheritance None, None, None, + None, $name::IS_AUTOMATIC_FORWARD, $name::HAS_PROTECTED_CONTENT, + $name::IS_FROM_OFFLINE, + None, + None, + None, $($field,)* // All of the other fields from the child struct ) } @@ -51,6 +65,7 @@ macro_rules! MessageCommon { // Rust was supposed to be used withot inheritance pub(crate) fn build_message_common(self, media_kind: MediaKind) -> Message { self.clone().build_message(MessageKind::Common(MessageCommon { author_signature: self.author_signature, + effect_id: self.effect_id, forward_origin: self.forward_origin, reply_to_message: self.reply_to_message, external_reply: self.external_reply, @@ -60,6 +75,10 @@ macro_rules! MessageCommon { // Rust was supposed to be used withot inheritance media_kind, is_automatic_forward: self.is_automatic_forward, has_protected_content: self.has_protected_content, + business_connection_id: self.business_connection_id, + is_from_offline: self.is_from_offline, + reply_to_story: self.reply_to_story, + sender_boost_count: self.sender_boost_count, })) } } @@ -126,6 +145,7 @@ MessageCommon! { pub struct MockMessageAnimation { pub caption: Option, pub caption_entities: Vec, + pub show_caption_above_media: bool, pub has_media_spoiler: bool, // Animation pub width: u32, @@ -143,6 +163,7 @@ MessageCommon! { impl MockMessageAnimation { pub const HAS_MEDIA_SPOILER: bool = false; + pub const SHOW_CAPTION_ABOVE_MEDIA: bool = false; pub const WIDTH: u32 = 50; pub const HEIGHT: u32 = 50; pub const DURATION: Seconds = Seconds::from_seconds(60); @@ -164,6 +185,7 @@ impl MockMessageAnimation { Self::new_message_common( None, vec![], + Self::SHOW_CAPTION_ABOVE_MEDIA, Self::HAS_MEDIA_SPOILER, Self::WIDTH, Self::HEIGHT, @@ -191,6 +213,7 @@ impl MockMessageAnimation { .build_message_common(MediaKind::Animation(MediaAnimation { caption: self.caption, caption_entities: self.caption_entities, + show_caption_above_media: self.show_caption_above_media, has_media_spoiler: self.has_media_spoiler, animation: Animation { file: FileMeta { @@ -559,7 +582,7 @@ MessageCommon! { pub latitude: f64, pub longitude: f64, pub horizontal_accuracy: Option, - pub live_period: Option, + pub live_period: Option, pub heading: Option, pub proximity_alert_radius: Option, } @@ -613,6 +636,7 @@ MessageCommon! { pub caption: Option, pub caption_entities: Vec, pub media_group_id: Option, + pub show_caption_above_media: bool, pub has_media_spoiler: bool, pub photo: Vec, } @@ -620,6 +644,7 @@ MessageCommon! { impl MockMessagePhoto { pub const HAS_MEDIA_SPOILER: bool = false; + pub const SHOW_CAPTION_ABOVE_MEDIA: bool = false; /// Creates a new easily changable message photo builder /// @@ -638,6 +663,7 @@ impl MockMessagePhoto { None, vec![], None, + Self::SHOW_CAPTION_ABOVE_MEDIA, Self::HAS_MEDIA_SPOILER, vec![MockPhotoSize::new().build()], ) @@ -658,6 +684,7 @@ impl MockMessagePhoto { caption: self.caption, caption_entities: self.caption_entities, media_group_id: self.media_group_id, + show_caption_above_media: self.show_caption_above_media, has_media_spoiler: self.has_media_spoiler, photo: self.photo, })) @@ -669,6 +696,7 @@ MessageCommon! { pub struct MockMessagePoll { pub poll_id: String, pub question: String, + pub question_entities: Option>, pub options: Vec, pub is_closed: bool, pub total_voter_count: u32, @@ -707,6 +735,7 @@ impl MockMessagePoll { Self::new_message_common( Self::POLL_ID.to_string(), Self::QUESTION.to_string(), + None, vec![], Self::IS_CLOSED, Self::TOTAL_VOTER_COUNT, @@ -736,6 +765,7 @@ impl MockMessagePoll { poll: Poll { id: self.poll_id, question: self.question, + question_entities: self.question_entities, options: self.options, is_closed: self.is_closed, total_voter_count: self.total_voter_count, @@ -845,6 +875,7 @@ MessageCommon! { pub caption: Option, pub caption_entities: Vec, pub media_group_id: Option, + pub show_caption_above_media: bool, pub has_media_spoiler: bool, pub video: Video, } @@ -852,6 +883,7 @@ MessageCommon! { impl MockMessageVideo { pub const HAS_MEDIA_SPOILER: bool = false; + pub const SHOW_CAPTION_ABOVE_MEDIA: bool = false; /// Creates a new easily changable message video builder /// @@ -867,6 +899,7 @@ impl MockMessageVideo { None, vec![], None, + Self::SHOW_CAPTION_ABOVE_MEDIA, Self::HAS_MEDIA_SPOILER, MockVideo::new().build(), ) @@ -887,6 +920,7 @@ impl MockMessageVideo { caption: self.caption, caption_entities: self.caption_entities, media_group_id: self.media_group_id, + show_caption_above_media: self.show_caption_above_media, has_media_spoiler: self.has_media_spoiler, video: self.video, })) diff --git a/teloxide_tests/src/dataset/mod.rs b/teloxide_tests/src/dataset/mod.rs index e81f010..ef8d7e4 100644 --- a/teloxide_tests/src/dataset/mod.rs +++ b/teloxide_tests/src/dataset/mod.rs @@ -1,42 +1,57 @@ //! A set of mocked structs for testing purposes. Read more in teloxide_tests crate. use std::sync::atomic::{AtomicI32, Ordering}; -use chrono::{DateTime, Utc}; use mime::Mime; use proc_macros::Changeable; use teloxide::types::{ - ChatFullInfo, ChatPhoto, FileMeta, LinkPreviewOptions, Location, Me, PhotoSize, Seconds, Update, User, UserId, Video + ChatPhoto, FileMeta, LinkPreviewOptions, LivePeriod, Location, Me, PhotoSize, Seconds, Update, + UpdateId, User, UserId, Video, }; pub mod chat; +pub mod chat_full_info; pub mod message; pub mod message_common; pub mod queries; +pub mod update; pub use chat::*; +pub use chat_full_info::*; pub use message::*; pub use message_common::*; pub use queries::*; use teloxide_tests_macros as proc_macros; +pub use update::*; #[cfg(test)] mod tests; pub trait IntoUpdate { /// Converts the mocked struct into an update vector, incrementing the id by 1 - fn into_update(self, id: AtomicI32) -> Vec; + fn into_update(self, id: &AtomicI32) -> Vec; } impl IntoUpdate for Vec where T: IntoUpdate, { - fn into_update(self, id: AtomicI32) -> Vec { + fn into_update(self, id: &AtomicI32) -> Vec { self.into_iter() - .map(|u| u.into_update(id.fetch_add(1, Ordering::Relaxed).into())) + .map(|u| { + id.fetch_add(1, Ordering::Relaxed); + u.into_update(id) + }) .flatten() .collect() } } +// Just to be able to use raw updates anywhere +impl IntoUpdate for Update { + fn into_update(mut self, id: &AtomicI32) -> Vec { + self.id = UpdateId(id.fetch_add(1, Ordering::Relaxed) as u32); + vec![self] + } +} + // // Structs below are just misc mocked structs // @@ -117,6 +132,7 @@ pub struct MockMe { pub can_join_groups: bool, pub can_read_all_group_messages: bool, pub supports_inline_queries: bool, + pub can_connect_to_business: bool, } impl MockMe { @@ -129,6 +145,7 @@ impl MockMe { pub const CAN_JOIN_GROUPS: bool = false; pub const CAN_READ_ALL_GROUP_MESSAGES: bool = false; pub const SUPPORTS_INLINE_QUERIES: bool = false; + pub const CAN_CONNECT_TO_BUSINESS: bool = false; /// Creates a new easily changable me builder /// @@ -151,6 +168,7 @@ impl MockMe { can_join_groups: Self::CAN_JOIN_GROUPS, can_read_all_group_messages: Self::CAN_READ_ALL_GROUP_MESSAGES, supports_inline_queries: Self::SUPPORTS_INLINE_QUERIES, + can_connect_to_business: Self::CAN_CONNECT_TO_BUSINESS, } } @@ -178,6 +196,7 @@ impl MockMe { can_join_groups: self.can_join_groups, can_read_all_group_messages: self.can_read_all_group_messages, supports_inline_queries: self.supports_inline_queries, + can_connect_to_business: self.can_connect_to_business, } } } @@ -243,7 +262,7 @@ pub struct MockLocation { pub latitude: f64, pub longitude: f64, pub horizontal_accuracy: Option, - pub live_period: Option, + pub live_period: Option, pub heading: Option, pub proximity_alert_radius: Option, } @@ -425,62 +444,6 @@ impl MockVideo { } } -#[derive(Changeable, Clone)] -pub struct MockChatFullInfo { - pub accent_color_id: Option, - pub background_custom_emoji_id: Option, - pub profile_accent_color_id: Option, - pub profile_background_custom_emoji_id: Option, - pub emoji_status_custom_emoji_id: Option, - pub emoji_status_expiration_date: Option>, - pub has_visible_history: bool, -} - -impl MockChatFullInfo { - /// Creates a new easily changable chat full info builder - /// - /// # Examples - /// ``` - /// let chat_full_info = teloxide_tests::MockChatFullInfo::new() - /// .accent_color_id(1) - /// .build(); - /// assert_eq!(chat_full_info.accent_color_id, Some(1)); - /// ``` - /// - pub fn new() -> Self { - Self { - accent_color_id: None, - background_custom_emoji_id: None, - profile_accent_color_id: None, - profile_background_custom_emoji_id: None, - emoji_status_custom_emoji_id: None, - emoji_status_expiration_date: None, - has_visible_history: true, - } - } - - /// Builds the chat full info - /// - /// # Examples - /// ``` - /// let mock_chat_full_info = teloxide_tests::MockChatFullInfo::new(); - /// let chat_full_info = mock_chat_full_info.build(); - /// assert_eq!(chat_full_info.has_visible_history, true); - /// ``` - /// - pub fn build(self) -> ChatFullInfo { - ChatFullInfo { - accent_color_id: self.accent_color_id, - background_custom_emoji_id: self.background_custom_emoji_id, - profile_accent_color_id: self.profile_accent_color_id, - profile_background_custom_emoji_id: self.profile_background_custom_emoji_id, - emoji_status_custom_emoji_id: self.emoji_status_custom_emoji_id, - emoji_status_expiration_date: self.emoji_status_expiration_date, - has_visible_history: self.has_visible_history, - } - } -} - #[derive(Changeable, Clone)] pub struct MockLinkPreviewOptions { pub is_disabled: bool, diff --git a/teloxide_tests/src/dataset/queries.rs b/teloxide_tests/src/dataset/queries.rs index 58218bb..7cc67d8 100644 --- a/teloxide_tests/src/dataset/queries.rs +++ b/teloxide_tests/src/dataset/queries.rs @@ -1,11 +1,9 @@ use std::sync::atomic::{AtomicI32, Ordering}; -use crate::proc_macros::Changeable; use teloxide::types::*; -use super::MockMessageText; - -use super::MockUser; +use super::{MockMessageText, MockUser}; +use crate::proc_macros::Changeable; #[derive(Changeable, Clone)] pub struct MockCallbackQuery { @@ -87,7 +85,7 @@ impl MockCallbackQuery { from: self.from, message: self.message.map(|message| { if !self.make_message_inaccessible { - MaybeInaccessibleMessage::Regular(message) + MaybeInaccessibleMessage::Regular(Box::new(message)) } else { MaybeInaccessibleMessage::Inaccessible(InaccessibleMessage { chat: message.chat, @@ -109,15 +107,17 @@ impl crate::dataset::IntoUpdate for MockCallbackQuery { /// # Example /// ``` /// use teloxide_tests::IntoUpdate; + /// use teloxide::types::{UpdateId, UpdateKind::CallbackQuery}; + /// use std::sync::atomic::AtomicI32; + /// /// let mock_callback_query = teloxide_tests::MockCallbackQuery::new(); - /// let update = mock_callback_query.clone().into_update(1.into())[0].clone(); - /// assert_eq!(update.id, teloxide::types::UpdateId(1)); - /// assert_eq!(update.kind, teloxide::types::UpdateKind::CallbackQuery( - /// mock_callback_query.build()) - /// ); + /// let update = mock_callback_query.clone().into_update(&AtomicI32::new(42))[0].clone(); + /// + /// assert_eq!(update.id, UpdateId(42)); + /// assert_eq!(update.kind, CallbackQuery(mock_callback_query.build())); /// ``` /// - fn into_update(self, id: AtomicI32) -> Vec { + fn into_update(self, id: &AtomicI32) -> Vec { vec![Update { id: UpdateId(id.fetch_add(1, Ordering::Relaxed) as u32), kind: UpdateKind::CallbackQuery(self.build()), diff --git a/teloxide_tests/src/dataset/tests.rs b/teloxide_tests/src/dataset/tests.rs index 4aab71e..915c41b 100644 --- a/teloxide_tests/src/dataset/tests.rs +++ b/teloxide_tests/src/dataset/tests.rs @@ -1,9 +1,10 @@ -use crate::dataset::*; -use crate::proc_macros::Changeable; use teloxide::{ dispatching::dialogue::GetChatId, - types::{ChatId, MessageEntity, MessageId, True, UpdateId, UserId}, + types::{ChatId, MessageEntity, MessageId, UpdateId, UpdateKind, UserId}, }; +use update::MockUpdatePoll; + +use crate::{dataset::*, proc_macros::Changeable}; #[derive(Changeable)] struct Test { @@ -62,42 +63,61 @@ fn test_location() { #[test] fn test_public_group_chat() { - let chat = MockGroupChat::new() - .title("Test") + let chat = MockGroupChat::new().title("Test").id(-1234); + let chat_photo = MockChatPhoto::new().build(); + let chat_full_info = MockChatFullInfoGroup::new() + .title("Test2") .id(-1234) - .photo(MockChatPhoto::new().build()); + .photo(chat_photo.clone()); let chat_object = chat.build(); assert_eq!(chat_object.title(), Some("Test")); assert_eq!(chat_object.id, ChatId(-1234)); - assert_eq!(chat_object.photo, Some(MockChatPhoto::new().build())); + + let chat_full_info_object = chat_full_info.build(); + assert_eq!(chat_full_info_object.title(), Some("Test2")); + assert_eq!(chat_full_info_object.id, ChatId(-1234)); + assert_eq!(chat_full_info_object.photo, Some(chat_photo)); } #[test] fn test_supergroup_chat() { - let chat = MockSupergroupChat::new().join_by_request(True).id(-1234); + let chat = MockSupergroupChat::new().id(-1234); + let chat_full_info = MockChatFullInfoSupergroup::new() + .join_by_request(true) + .id(-1234); let chat_object = chat.build(); assert_eq!(chat_object.id, ChatId(-1234)); - assert_eq!(chat_object.join_by_request(), Some(True)); + + let chat_full_info_object = chat_full_info.build(); + assert_eq!(chat_full_info_object.id, ChatId(-1234)); + assert_eq!(chat_full_info_object.join_by_request(), true); } #[test] fn test_channel_chat() { - let chat = MockChannelChat::new() - .linked_chat_id(-12345) + let chat = MockChannelChat::new().username("test_channel").id(-1234); + let chat_full_info = MockChatFullInfoChannel::new() .username("test_channel") + .linked_chat_id(-12345) .id(-1234); let chat_object = chat.build(); assert_eq!(chat_object.id, ChatId(-1234)); - assert_eq!(chat_object.linked_chat_id(), Some(-12345)); assert_eq!(chat_object.username(), Some("test_channel")); + + let chat_full_info_object = chat_full_info.build(); + assert_eq!(chat_full_info_object.id, ChatId(-1234)); + assert_eq!(chat_full_info_object.username(), Some("test_channel")); + assert_eq!(chat_full_info_object.linked_chat_id(), Some(-12345)); } #[test] fn test_private_group_chat() { - let chat = MockPrivateChat::new() + let chat = MockPrivateChat::new().first_name("Test").id(1234); + + let chat_full_info = MockChatFullInfoPrivate::new() .first_name("Test") .id(1234) .bio("Test bio"); @@ -105,7 +125,11 @@ fn test_private_group_chat() { let chat_object = chat.build(); assert_eq!(chat_object.first_name(), Some("Test")); assert_eq!(chat_object.id, ChatId(1234)); - assert_eq!(chat_object.bio(), Some("Test bio")); + + let chat_full_info_object = chat_full_info.build(); + assert_eq!(chat_full_info_object.first_name(), Some("Test")); + assert_eq!(chat_full_info_object.id, ChatId(1234)); + assert_eq!(chat_full_info_object.bio(), Some("Test bio")); } // @@ -150,9 +174,9 @@ fn test_message_common_text() { fn test_into_update() { let message = MockMessageText::new().text("text"); - let update = message.into_update(1.into())[0].clone(); + let update = message.into_update(&AtomicI32::new(42))[0].clone(); - assert_eq!(update.id, UpdateId(1)); + assert_eq!(update.id, UpdateId(42)); assert_eq!(update.chat_id(), Some(ChatId(MockUser::ID as i64))); } @@ -376,3 +400,22 @@ fn test_callback_query() { assert_eq!(query_object.id, MockCallbackQuery::ID); assert_eq!(query_object.from.first_name, MockUser::FIRST_NAME); } + +// +// +// + +#[test] +fn test_update_poll() { + let update = MockUpdatePoll::new().poll_id("123"); + + let update_object = update.into_update(&AtomicI32::new(1))[0].clone(); + + if let UpdateKind::Poll(poll) = update_object.kind { + assert_eq!(poll.question, MockMessagePoll::QUESTION); + assert_eq!(poll.poll_type, MockMessagePoll::POLL_TYPE); + assert_eq!(poll.id, "123".to_owned()); + } else { + unreachable!() + } +} diff --git a/teloxide_tests/src/dataset/update.rs b/teloxide_tests/src/dataset/update.rs new file mode 100644 index 0000000..ce610dd --- /dev/null +++ b/teloxide_tests/src/dataset/update.rs @@ -0,0 +1,82 @@ +use std::sync::atomic::Ordering; + +use chrono::{DateTime, Utc}; +use teloxide::types::{ + MessageEntity, Poll, PollOption, PollType, Seconds, Update, UpdateId, UpdateKind, +}; +use teloxide_tests_macros::Changeable; + +use super::{IntoUpdate, MockMessagePoll}; + +#[derive(Changeable, Clone)] +pub struct MockUpdatePoll { + pub poll_id: String, + pub question: String, + pub question_entities: Option>, + pub options: Vec, + pub is_closed: bool, + pub total_voter_count: u32, + pub is_anonymous: bool, + pub poll_type: PollType, + pub allows_multiple_answers: bool, + pub correct_option_id: Option, + pub explanation: Option, + pub explanation_entities: Option>, + pub open_period: Option, + pub close_date: Option>, +} + +impl MockUpdatePoll { + /// Creates a new easily changable poll update builder + /// + /// # Example + /// ``` + /// let update = teloxide_tests::MockUpdatePoll::new() + /// .poll_id("123456"); + /// + /// assert_eq!(update.poll_id, "123456"); + /// ``` + pub fn new() -> Self { + let poll = MockMessagePoll::new(); + Self { + poll_id: poll.poll_id, + question: poll.question, + question_entities: poll.question_entities, + options: poll.options, + is_closed: poll.is_closed, + total_voter_count: poll.total_voter_count, + is_anonymous: poll.is_anonymous, + poll_type: poll.poll_type, + allows_multiple_answers: poll.allows_multiple_answers, + correct_option_id: poll.correct_option_id, + explanation: poll.explanation, + explanation_entities: poll.explanation_entities, + open_period: poll.open_period, + close_date: poll.close_date, + } + } +} + +impl IntoUpdate for MockUpdatePoll { + fn into_update(self, id: &std::sync::atomic::AtomicI32) -> Vec { + vec![Update { + id: UpdateId(id.fetch_add(1, Ordering::Relaxed) as u32), + kind: UpdateKind::Poll(Poll { + id: self.poll_id, + question: self.question, + question_entities: self.question_entities, + options: self.options, + is_closed: self.is_closed, + total_voter_count: self.total_voter_count, + is_anonymous: self.is_anonymous, + poll_type: self.poll_type, + allows_multiple_answers: self.allows_multiple_answers, + correct_option_id: self.correct_option_id, + explanation: self.explanation, + explanation_entities: self.explanation_entities, + open_period: self.open_period, + close_date: self.close_date, + }), + }] + } +} diff --git a/teloxide_tests/src/lib.rs b/teloxide_tests/src/lib.rs index 80b2275..67c71b5 100644 --- a/teloxide_tests/src/lib.rs +++ b/teloxide_tests/src/lib.rs @@ -36,7 +36,7 @@ //! //! #[tokio::test] //! async fn test_hello_world() { // A testing bot dispatch -//! let bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); +//! let mut bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); //! bot.dispatch().await; //! let message = bot.get_responses().sent_messages.last().unwrap(); //! // This is a regular teloxide::types::Message! @@ -58,6 +58,7 @@ //! //! - /AnswerCallbackQuery //! - /DeleteMessage +//! - /DeleteMessages //! - /EditMessageText //! - /EditMessageReplyMarkup //! - /EditMessageCaption @@ -78,6 +79,7 @@ //! - /SendSticker //! - /SendChatAction //! - /SendMediaGroup +//! - /SendInvoice //! - /PinChatMessage //! - /UnpinChatMessage //! - /UnpinAllChatMessages @@ -87,10 +89,12 @@ //! - /UnbanChatMember //! - /RestrictChatMember //! - /SetMessageReaction +//! - /SetMyCommands +//! - /GetMe //! //! More endpoints will be added as time goes on! //! -//! (do not worry about /GetMe and /GetUpdates, they are not needed for this bot!) +//! (/GetUpdates and /GetWebhookInfo exist, but they are dummies) //! //! And also fake file downloading! //! @@ -140,11 +144,31 @@ html_logo_url = "https://github.com/user-attachments/assets/627beca8-5852-4c70-97e0-5f4fcb5e2040", html_favicon_url = "https://github.com/user-attachments/assets/627beca8-5852-4c70-97e0-5f4fcb5e2040" )] +#![allow(clippy::too_long_first_doc_paragraph)] +#![allow(clippy::to_string_in_format_args)] +#![allow(clippy::new_without_default)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::map_flatten)] +#![allow(clippy::unnecessary_unwrap)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::needless_borrows_for_generic_args)] +#![allow(clippy::search_is_some)] +#![allow(clippy::unwrap_or_default)] +#![allow(clippy::enum_variant_names)] +#![allow(clippy::needless_return)] +#![allow(clippy::bool_assert_comparison)] + mod dataset; +pub(crate) mod listener; pub mod mock_bot; -mod server; +pub mod server; +pub(crate) mod state; #[cfg(test)] mod tests; +pub(crate) mod utils; pub use dataset::*; pub use mock_bot::MockBot; diff --git a/teloxide_tests/src/listener.rs b/teloxide_tests/src/listener.rs new file mode 100644 index 0000000..95d9f1b --- /dev/null +++ b/teloxide_tests/src/listener.rs @@ -0,0 +1,67 @@ +use std::{ + pin::Pin, + sync::Mutex, + task::{Context, Poll}, + thread::sleep, + time::Duration, +}; + +use futures_util::Stream; +use teloxide::{ + stop::StopToken, + types::Update, + update_listeners::{AsUpdateStream, UpdateListener}, + Bot, RequestError, +}; + +// It isn't really a listener, it just takes the updates and feeds them one by one to the +// dispather, until there is no more. +pub(crate) struct InsertingListener { + pub updates: Vec, +} + +pub(crate) struct InsertingListenerStream { + updates: Mutex>, +} + +impl Stream for InsertingListenerStream { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + if self.updates.lock().unwrap().len() == 0 { + // A small wait to make sure the state is setteled in?.. + // No idea, but it fixes a bug with test_erased_state... + sleep(Duration::from_millis(10)); + // Returning Poll::Ready(None) means that there is nothing more to poll, and the + // dispatcher closes. + return Poll::Ready(None); + } + // Returns updates one by one + let update = self.updates.lock().unwrap().remove(0); + Poll::Ready(Some(Ok(update))) + } +} + +impl UpdateListener for InsertingListener { + type Err = RequestError; + + fn stop_token(&mut self) -> StopToken { + // This is a workaround, StopToken fields are private + let token = "1234567890:QWERTYUIOPASDFGHJKLZXCVBNMQWERTYUIO"; + let bot = Bot::new(token); + teloxide::update_listeners::Polling::builder(bot) + .build() + .stop_token() + } +} + +impl<'a> AsUpdateStream<'a> for InsertingListener { + type StreamErr = RequestError; + type Stream = InsertingListenerStream; + + fn as_stream(&'a mut self) -> Self::Stream { + InsertingListenerStream { + updates: self.updates.clone().into(), + } + } +} diff --git a/teloxide_tests/src/mock_bot.rs b/teloxide_tests/src/mock_bot.rs index c3b516e..ffa3b58 100644 --- a/teloxide_tests/src/mock_bot.rs +++ b/teloxide_tests/src/mock_bot.rs @@ -1,132 +1,77 @@ //! Mock bot that sends requests to the fake server -use gag::Gag; -use serde_json::Value; use std::{ env, + fmt::Debug, + hash::Hash, mem::discriminant, panic, sync::{atomic::AtomicI32, Arc, Mutex, MutexGuard, PoisonError}, }; -use teloxide::{ - dispatching::dialogue::ErasedStorage, - dptree::di::DependencySupplier, - types::{File, FileMeta, MaybeInaccessibleMessage, MessageId, MessageKind}, -}; -use teloxide::{dptree::deps, types::UpdateKind}; -use tokio::task::JoinHandle; -use crate::dataset::{IntoUpdate, MockMe}; -use crate::server::{self, Responses, FILES, MESSAGES}; +use gag::Gag; +use lazy_static::lazy_static; use teloxide::{ dispatching::{ - dialogue::{GetChatId, InMemStorage, Storage}, + dialogue::{ErasedStorage, GetChatId, InMemStorage, Storage}, UpdateHandler, }, + dptree::di::DependencySupplier, + error_handlers::ErrorHandler, prelude::*, - types::Me, + types::{MaybeInaccessibleMessage, Me, UpdateKind}, }; -static GET_POTENTIAL_STORAGE_LOCK: Mutex<()> = Mutex::new(()); -static BOT_LOCK: Mutex<()> = Mutex::new(()); - -fn find_file(value: Value) -> Option { - // Recursively searches for file meta - let mut file_id = None; - let mut file_unique_id = None; - let mut file_size = None; - if let Value::Object(map) = value { - for (k, v) in map { - if k == "file_id" { - file_id = Some(v.as_str().unwrap().to_string()); - } else if k == "file_unique_id" { - file_unique_id = Some(v.as_str().unwrap().to_string()); - } else if k == "file_size" { - file_size = Some(v.as_u64().unwrap() as u32); - } else if let Some(found) = find_file(v) { - return Some(found); - } - } - } - if file_id.is_some() && file_unique_id.is_some() { - return Some(FileMeta { - id: file_id.unwrap(), - unique_id: file_unique_id.unwrap(), - size: file_size.unwrap_or(0), - }); - } - None -} - -fn find_chat_id(value: Value) -> Option { - // Recursively searches for chat id - if let Value::Object(map) = value { - for (k, v) in map { - if k == "chat" { - return Some(v["id"].as_i64()?); - } else if let Some(found) = find_chat_id(v) { - return Some(found); - } - } - } - None -} +// Needed for trait bound stuff +pub use crate::utils::DistributionKey; +use crate::{ + dataset::{IntoUpdate, MockMe}, + listener::InsertingListener, + server, + server::ServerManager, + state::State, + utils::{assert_eqn, default_distribution_function, find_chat_id}, +}; -fn add_message(message: &mut Message) { - let max_id = MESSAGES.max_message_id(); - if message.id.0 <= max_id || MESSAGES.get_message(message.id.0).is_some() { - message.id = MessageId(max_id + 1); - } - if let Some(file_meta) = find_file(serde_json::to_value(&message).unwrap()) { - let file = File { - meta: file_meta, - path: "some_path.txt".to_string(), // This doesn't really matter - }; - FILES.lock().unwrap().push(file); - } - if let MessageKind::Common(ref mut message_kind) = message.kind { - if let Some(ref mut reply_message) = message_kind.reply_to_message { - add_message(reply_message); - } - } - MESSAGES.add_message(message.clone()); +lazy_static! { + static ref BOT_LOCK: Mutex<()> = Mutex::new(()); } -async fn stop_server() { - let client = reqwest::Client::new(); - let _ = client - .post(format!( - "http://127.0.0.1:{}/stop/false", - MockBot::PORT.lock().unwrap().clone() - )) - .send() - .await; -} +const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; /// A mocked bot that sends requests to the fake server -/// Please check the `new` function docs and [github examples](https://github.com/LasterAlex/teloxide_tests/tree/master/examples) for more information. -#[allow(dead_code)] -pub struct MockBot { +/// Please check the [`new`] function docs and [github examples](https://github.com/LasterAlex/teloxide_tests/tree/master/examples) for more information. +/// +/// If you are having troubles with generics while trying to store `MockBot`, just do this: +/// +/// `MockBot, teloxide_tests::mock_bot::DistributionKey>` +/// +/// [`new`]: crate::MockBot::new +pub struct MockBot { /// The bot with a fake server url pub bot: Bot, /// The thing that dptree::entry() returns - pub handler_tree: UpdateHandler>, - // Mutexes is here to not worry about mut references, its easier for the user without them + pub handler_tree: UpdateHandler, /// Updates to send as user - pub updates: Mutex>, + pub updates: Vec, /// Bot parameters are here - pub me: Mutex, + pub me: Me, /// If you have something like a state, you should add the storage here using .dependencies() - pub dependencies: Mutex, - /// Caught responses from the server - pub responses: Mutex>, - bot_lock: Mutex>>, // Maybe in the future ill make something like an atomic - // bool that says if the bot is locked or not, and implement a custom Drop trait -} + pub dependencies: DependencyMap, + /// The stack size of the runtime for running updates + pub stack_size: usize, -impl MockBot { - const CURRENT_UPDATE_ID: AtomicI32 = AtomicI32::new(0); // So that every update is different - const PORT: Mutex = Mutex::new(6504); + distribution_f: fn(&Update) -> Option, + error_handler: Arc + Send + Sync>, + current_update_id: AtomicI32, + state: Arc>, + _bot_lock: Option>, +} + +impl MockBot +where + Err: Debug + Send + Sync + 'static, +{ /// Creates a new MockBot, using something that can be turned into Updates, and a handler tree. /// You can't create a new bot while you have another bot in scope. Otherwise you will have a /// lot of race conditions. If you still somehow manage to create two bots at the same time @@ -162,7 +107,7 @@ impl MockBot { /// /// #[tokio::main] // Change for tokio::test in your implementation /// async fn main() { - /// let bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); + /// let mut bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); /// bot.dispatch().await; /// let responses = bot.get_responses(); /// let message = responses @@ -176,39 +121,86 @@ impl MockBot { pub fn new( update: T, // This 'T' is just anything that can be turned into an Update, like a // MockMessageText or MockCallbackQuery, or a vec[MockMessagePhoto] if you want! - handler_tree: UpdateHandler>, + handler_tree: UpdateHandler, ) -> Self where T: IntoUpdate, // And that code just "proves" that it can be turned into an update + Err: Debug, { - unsafe { - env::set_var( - // So that teloxide bot doesn't complain - "TELOXIDE_TOKEN", - "1234567890:QWERTYUIOPASDFGHJKLZXCVBNMQWERTYUIO", - ); - } let _ = pretty_env_logger::try_init(); - let bot = Bot::from_env().set_api_url( - reqwest::Url::parse(&format!( - "http://localhost:{}", - Self::PORT.lock().unwrap().clone() - )) - .unwrap(), - ); - let lock = BOT_LOCK.lock().unwrap_or_else(PoisonError::into_inner); + let token = "1234567890:QWERTYUIOPASDFGHJKLZXCVBNMQWERTYUIO"; + let bot = Bot::new(token); + let current_update_id = AtomicI32::new(42); + let state = Arc::new(Mutex::new(State::default())); + // If the lock is poisoned, we don't care, some other bot panicked and can't do anything + let lock = Some(BOT_LOCK.lock().unwrap_or_else(PoisonError::into_inner)); + Self { bot, - me: Mutex::new(MockMe::new().build()), - updates: Mutex::new(update.into_update(Self::CURRENT_UPDATE_ID)), + me: MockMe::new().build(), + updates: update.into_update(¤t_update_id), handler_tree, - responses: Mutex::new(None), - dependencies: Mutex::new(DependencyMap::new()), - bot_lock: Mutex::new(Some(lock)), // This makes a lock that forbids the creation of - // other bots until this one goes out of scope. That way there will be no race - // conditions! + dependencies: DependencyMap::new(), + stack_size: DEFAULT_STACK_SIZE, + error_handler: LoggingErrorHandler::new(), + distribution_f: default_distribution_function, + _bot_lock: lock, + current_update_id, + state, + } + } +} + +// Trait bound things. +impl MockBot +where + Err: Debug + Send + Sync + 'static, + Key: Hash + Eq + Clone + Send + 'static, +{ + /// Same as [`new`], but it inserts a distribution_function into the dispatcher + /// + /// [`new`]: crate::MockBot::new + // It is its own function instead of `.distribution_function` setter because of the Key + // generic. If `new` sets the Key to DefaultKey, it's impossible to swich back to a different + // one, even if it fits all the trait bounds. + pub fn new_with_distribution_function( + update: T, + handler_tree: UpdateHandler, + f: fn(&Update) -> Option, + ) -> Self + where + T: IntoUpdate, + Err: Debug, + { + // Again, trait bounds stuff, the generic Key is hard to work around + let MockBot { + bot, + me, + updates, + handler_tree, + dependencies, + stack_size, + error_handler, + distribution_f: _, + _bot_lock, + current_update_id, + state, + } = MockBot::new(update, handler_tree); + + Self { + bot, + me, + updates, + handler_tree, + dependencies, + stack_size, + error_handler, + distribution_f: f, + _bot_lock, + current_update_id, + state, } } @@ -216,161 +208,114 @@ impl MockBot { /// Just like in this teloxide example: /// You can use it to add dependencies to your handler tree. /// For more examples - look into `get_state` method documentation - pub fn dependencies(&self, deps: DependencyMap) { - *self.dependencies.lock().unwrap() = deps; + pub fn dependencies(&mut self, deps: DependencyMap) { + self.dependencies = deps; } /// Sets the bot parameters, like supports_inline_queries, first_name, etc. - pub fn me(&self, me: MockMe) { - *self.me.lock().unwrap() = me.build(); + pub fn me(&mut self, me: MockMe) { + self.me = me.build(); } /// Sets the updates. Useful for reusing the same mocked bot instance in different tests - /// Reminder: You can pass in vec![MockMessagePhoto] or something else! - pub fn update(&self, update: T) { - *self.updates.lock().unwrap() = update.into_update(Self::CURRENT_UPDATE_ID); + /// Reminder: You can pass in `vec![MockMessagePhoto]` or something else! + pub fn update(&mut self, update: T) { + self.updates = update.into_update(&self.current_update_id); + } + + /// Sets the error_handler for Dispather + pub fn error_handler(&mut self, handler: Arc + Send + Sync>) { + self.error_handler = handler; } - fn collect_handles(&self, handles: &mut Vec>) { - let updates_lock = self.updates.lock().unwrap().clone(); - let self_deps = self.dependencies.lock().unwrap().clone(); - for mut update_lock in updates_lock { - match update_lock.kind.clone() { + /// Just inserts the updates into the state, returning them + fn insert_updates(&self, updates: &mut [Update]) { + for update in updates.iter_mut() { + match update.kind.clone() { UpdateKind::Message(mut message) => { // Add the message to the list of messages, so the bot can interact with it - add_message(&mut message); - update_lock.kind = UpdateKind::Message(message.clone()); + self.state.lock().unwrap().add_message(&mut message); + update.kind = UpdateKind::Message(message.clone()); + } + UpdateKind::EditedMessage(mut message) => { + self.state.lock().unwrap().edit_message(&mut message); + update.kind = UpdateKind::EditedMessage(message.clone()); } UpdateKind::CallbackQuery(mut callback) => { if let Some(MaybeInaccessibleMessage::Regular(ref mut message)) = callback.message { - add_message(message); + self.state.lock().unwrap().add_message(message); } - update_lock.kind = UpdateKind::CallbackQuery(callback.clone()); + update.kind = UpdateKind::CallbackQuery(callback.clone()); } _ => {} } - - let mut deps = deps![ - self.bot.clone(), - self.me.lock().unwrap().clone(), - update_lock.clone() // This actually makes an update go through the dptree - ]; - - deps.insert_container(self_deps.clone()); // These are nessessary for the dispatch - - // This, too, will need to be redone in the ideal world, but it just waits until the server is up - let handler_tree = self.handler_tree.clone(); - - handles.push(tokio::spawn(async move { - let result = handler_tree.dispatch(deps.clone()).await; - if let ControlFlow::Break(result) = result { - // If it returned `ControlFlow::Break`, everything is fine, but we need to check, if the - // handler didn't error out - assert!(result.is_ok(), "Error in handler: {:?}", result); - } else { - log::error!("Update didn't get handled!"); - panic!("Unhandled update!"); - } - })); } } - async fn close_bot(&self) { - stop_server().await; - *self.bot_lock.lock().unwrap() = None; + async fn run_updates(&self, bot: Bot, updates: Vec) { + let handler_tree = self.handler_tree.clone(); + let deps = self.dependencies.clone(); + let stack_size = self.stack_size; + let distribution_f = self.distribution_f.clone(); + let error_handler = self.error_handler.clone(); + + tokio::task::spawn_blocking(move || { + let runtime = tokio::runtime::Builder::new_multi_thread() + .thread_stack_size(stack_size) // Not needed, but just in case + .enable_all() + .build() + .unwrap(); + runtime.block_on(async { + Dispatcher::builder(bot.clone(), handler_tree.clone()) + .dependencies(deps) + .distribution_function(distribution_f) + .error_handler(error_handler) + .build() + .dispatch_with_listener( + InsertingListener { updates }, + LoggingErrorHandler::new(), + ) + .await; + }); + }) + .await + .expect("Dispatcher panicked!"); } /// Actually dispatches the bot, calling the update through the handler tree. /// All the requests made through the bot will be stored in `responses`, and can be retrieved /// with `get_responses`. All the responses are unique to that dispatch, and will be erased for /// every new dispatch. - pub async fn dispatch(&self) { - let runtime = tokio::runtime::Handle::current(); - // If the user presses ctrl-c, the server will be shut down - let _ = ctrlc::set_handler(move || { - runtime.block_on(stop_server()); - std::process::exit(1); - }); - - // In the future, this will need to be redone nicely, but right now it works. - // It prevents a race condition for different bot instances to try to use the same server - // (like in docstring) - stop_server().await; - let mut left_tries = 200; - while reqwest::get(format!( - "http://127.0.0.1:{}/ping", - Self::PORT.lock().unwrap().clone() - )) - .await - .is_ok() - { - left_tries -= 1; - if left_tries == 0 { - self.close_bot().await; - panic!( - "Failed to unbind the server on the port {}!", - Self::PORT.lock().unwrap().clone() - ); - } - } + /// + /// This method overrides env variables `TELOXIDE_TOKEN` and `TELOXIDE_API_URL`, so anyone can + /// call `Bot::from_env()` and get an actual bot that is connected to the fake server + pub async fn dispatch(&mut self) { + self.state.lock().unwrap().reset(); - let server = tokio::spawn(server::main(Self::PORT, self.me.lock().unwrap().clone())); // This starts the server in the background + let server = ServerManager::start(self.me.clone(), self.state.clone()) + .await + .unwrap(); - let mut left_tries = 200; - while reqwest::get(format!( - "http://127.0.0.1:{}/ping", - Self::PORT.lock().unwrap().clone() - )) - .await - .is_err() - { - left_tries -= 1; - if left_tries == 0 { - self.close_bot().await; - panic!( - "Failed to get the server on the port {}!", - Self::PORT.lock().unwrap().clone() - ); - } - } + let mut updates = self.updates.clone(); + self.insert_updates(&mut updates); - // Gets all of the updates to send - let mut handles = vec![]; - self.collect_handles(&mut handles); - - for handle in handles { - // Waits until every update has been sent - match handle.await { - Ok(_) => {} - Err(_) => { - // Something panicked, we need to free the bot lock and exit - self.close_bot().await; - panic!("Something went wrong and the bot panicked!"); - } - }; - } + let api_url = reqwest::Url::parse(&format!("http://127.0.0.1:{}", server.port)).unwrap(); + let bot = self.bot.clone().set_api_url(api_url.clone()); + + env::set_var("TELOXIDE_TOKEN", bot.token()); + env::set_var("TELOXIDE_API_URL", api_url.to_string()); - *self.responses.lock().unwrap() = Some(server::RESPONSES.lock().unwrap().clone()); // Store the responses - // before they are erased + self.run_updates(bot, updates).await; - stop_server().await; - server.await.unwrap(); // Waits before the server is shut down + server.stop().await.unwrap(); } /// Returns the responses stored in `responses` - /// Panics if no dispatching was done. /// Should be treated as a variable, because it kinda is pub fn get_responses(&self) -> server::Responses { - let responses = self.responses.lock().unwrap().clone(); - match responses { - Some(responses) => responses, - None => { - log::error!("No responses received! Maybe you forgot to dispatch the mocked bot?"); - panic!("No responses received! Maybe you forgot to dispatch the mocked bot?") - } - } + self.state.lock().unwrap().responses.clone() } async fn get_potential_storages( @@ -382,14 +327,11 @@ impl MockBot { where S: Send + 'static + Clone, { - let get_potential_storage_lock = GET_POTENTIAL_STORAGE_LOCK.lock(); - // If not this lock, some panic messages will make it to stderr, even with gag, because - // race condition. let default_panic = panic::take_hook(); let in_mem_storage: Option>>>; let erased_storage: Option>>>; // No trace storage cuz who uses it - let dependencies = Arc::new(self.dependencies.lock().unwrap().clone()); + let dependencies = Arc::new(self.dependencies.clone()); // Get dependencies into Arc cuz otherwise it complaints about &self being moved panic::set_hook(Box::new(|_| { @@ -403,7 +345,7 @@ impl MockBot { .join() .ok(); - let dependencies = Arc::new(self.dependencies.lock().unwrap().clone()); + let dependencies = Arc::new(self.dependencies.clone()); // Dependencies were moved to a prev. thread, so create a new one erased_storage = std::thread::spawn(move || { // The same for ErasedStorage @@ -414,7 +356,6 @@ impl MockBot { panic::set_hook(default_panic); // Restore the default panic hook drop(print_gag); - drop(get_potential_storage_lock); (in_mem_storage, erased_storage) } @@ -461,7 +402,7 @@ impl MockBot { /// /// #[tokio::main] /// async fn main() { - /// let bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); + /// let mut bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree()); /// bot.dependencies(deps![InMemStorage::::new()]); /// bot.set_state(State::Start).await; /// // Yes, Start is the default state, but this just shows how it works @@ -486,15 +427,13 @@ impl MockBot { S: Send + 'static + Clone, { let (in_mem_storage, erased_storage) = self.get_potential_storages().await; - let updates = self.updates.lock().unwrap().clone(); - let update_lock = updates.first().expect("No updates were detected!"); - let chat_id = match update_lock.chat_id() { + let first_update = self.updates.first().expect("No updates were detected!"); + let chat_id = match first_update.chat_id() { Some(chat_id) => chat_id, - None => match find_chat_id(serde_json::to_value(&update_lock).unwrap()) { + None => match find_chat_id(serde_json::to_value(first_update).unwrap()) { Some(id) => ChatId(id), None => { log::error!("No chat id was detected in the update! Did you send an update without a chat identifier? Like MockCallbackQuery without an attached message?"); - self.close_bot().await; panic!("No chat id was detected!"); } }, @@ -514,29 +453,46 @@ impl MockBot { .await .expect("Failed to update dialogue"); } else { - self.close_bot().await; log::error!("No storage was detected! Did you add it to bot.dependencies(deps![get_bot_storage().await]); ?"); panic!("No storage was detected!"); } } + /// Helper function to fetch the state of the dialogue and assert its value + pub async fn assert_state(&self, state: S) + where + S: Send + Default + 'static + Clone + Debug + PartialEq, + { + assert_eqn!(self.get_state::().await, state, "States are not equal!") + } + /// Gets the state of the dialogue, if the storage exists in dependencies /// Panics if no storage was found - /// You need to use type annotation to get the state, please refer to the `set_state` + /// You need to use type annotation to get the state, please refer to the [`set_state`] /// documentation example + /// + /// [`set_state`]: crate::MockBot::set_state pub async fn get_state(&self) -> S + where + S: Send + Default + 'static + Clone, + { + self.try_get_state().await.unwrap_or(S::default()) + } + + /// Same as [`get_state`], but returns None if the state is None, instead of the default + /// + /// [`get_state`]: crate::MockBot::get_state + pub async fn try_get_state(&self) -> Option where S: Send + 'static + Clone, { let (in_mem_storage, erased_storage) = self.get_potential_storages().await; - let updates = self.updates.lock().unwrap().clone(); - let update_lock = updates.first().expect("No updates were detected!"); - let chat_id = match update_lock.chat_id() { + let first_update = self.updates.first().expect("No updates were detected!"); + let chat_id = match first_update.chat_id() { Some(chat_id) => chat_id, - None => match find_chat_id(serde_json::to_value(&update_lock).unwrap()) { + None => match find_chat_id(serde_json::to_value(first_update).unwrap()) { Some(id) => ChatId(id), None => { - *self.bot_lock.lock().unwrap() = None; panic!("No chat id was detected!"); } }, @@ -547,16 +503,16 @@ impl MockBot { .clone() .get_dialogue(chat_id) .await - .expect("Error getting dialogue") - .expect("State is None") + .ok() + .flatten() } else if let Some(storage) = erased_storage { // If erased storage exists (*storage) .clone() .get_dialogue(chat_id) .await - .expect("Error getting dialogue") - .expect("State is None") + .ok() + .flatten() } else { log::error!("No storage was detected! Did you add it to bot.dependencies(deps![get_bot_storage().await]); ?"); panic!("No storage was detected!"); @@ -569,7 +525,7 @@ impl MockBot { /// Dispatches and checks the last sent message text or caption. Pass in an empty string if you /// want the text or caption to be None - pub async fn dispatch_and_check_last_text(&self, text_or_caption: &str) { + pub async fn dispatch_and_check_last_text(&mut self, text_or_caption: &str) { self.dispatch().await; let responses = self.get_responses(); @@ -579,9 +535,9 @@ impl MockBot { .expect("No sent messages were detected!"); if let Some(text) = message.text() { - assert_eq!(text, text_or_caption, "Texts are not equal!"); + assert_eqn!(text, text_or_caption, "Texts are not equal!"); } else if let Some(caption) = message.caption() { - assert_eq!(caption, text_or_caption, "Captions are not equal!"); + assert_eqn!(caption, text_or_caption, "Captions are not equal!"); } else if !text_or_caption.is_empty() { panic!("Message has no text or caption!"); } @@ -589,9 +545,12 @@ impl MockBot { /// Same as `dispatch_and_check_last_text`, but also checks the state. You need to derive /// PartialEq, Clone and Debug for the state like in `set_state` example - pub async fn dispatch_and_check_last_text_and_state(&self, text_or_caption: &str, state: S) - where - S: Send + 'static + Clone + std::fmt::Debug + PartialEq, + pub async fn dispatch_and_check_last_text_and_state( + &mut self, + text_or_caption: &str, + state: S, + ) where + S: Send + Default + 'static + Clone + std::fmt::Debug + PartialEq, { self.dispatch().await; @@ -602,26 +561,25 @@ impl MockBot { .expect("No sent messages were detected!"); if let Some(text) = message.text() { - assert_eq!(text, text_or_caption, "Texts are not equal!"); + assert_eqn!(text, text_or_caption, "Texts are not equal!"); } else if let Some(caption) = message.caption() { - assert_eq!(caption, text_or_caption, "Captions are not equal!"); + assert_eqn!(caption, text_or_caption, "Captions are not equal!"); } else if !text_or_caption.is_empty() { panic!("Message has no text or caption!"); } - let got_state: S = self.get_state().await; - assert_eq!(got_state, state, "States are not equal!"); + self.assert_state(state).await; } /// Same as `dispatch_and_check_last_text`, but also checks, if the variants of the state are the same /// /// For example, `State::Start { some_field: "value" }` and `State::Start { some_field: "other value" }` are the same in this function pub async fn dispatch_and_check_last_text_and_state_discriminant( - &self, + &mut self, text_or_caption: &str, state: S, ) where - S: Send + 'static + Clone, + S: Send + PartialEq + Debug + Default + 'static + Clone, { self.dispatch().await; @@ -632,42 +590,37 @@ impl MockBot { .expect("No sent messages were detected!"); if let Some(text) = message.text() { - assert_eq!(text, text_or_caption, "Texts are not equal!"); + assert_eqn!(text, text_or_caption, "Texts are not equal!"); } else if let Some(caption) = message.caption() { - assert_eq!(caption, text_or_caption, "Captions are not equal!"); + assert_eqn!(caption, text_or_caption, "Captions are not equal!"); } else if !text_or_caption.is_empty() { panic!("Message has no text or caption!"); } let got_state: S = self.get_state().await; - assert_eq!( - discriminant(&got_state), - discriminant(&state), - "State variants are not equal!" - ); + if discriminant(&got_state) != discriminant(&state) { + assert_eqn!(got_state, state, "State variants are not equal!") + } } /// Just checks the state after dispathing the update, like `dispatch_and_check_last_text_and_state` - pub async fn dispatch_and_check_state(&self, state: S) + pub async fn dispatch_and_check_state(&mut self, state: S) where - S: Send + 'static + Clone + std::fmt::Debug + PartialEq, + S: Send + Default + 'static + Clone + std::fmt::Debug + PartialEq, { self.dispatch().await; - let got_state: S = self.get_state().await; - assert_eq!(got_state, state, "States are not equal!"); + self.assert_state(state).await; } /// Just checks the state discriminant after dispathing the update, like `dispatch_and_check_last_text_and_state_discriminant` - pub async fn dispatch_and_check_state_discriminant(&self, state: S) + pub async fn dispatch_and_check_state_discriminant(&mut self, state: S) where - S: Send + 'static + Clone, + S: Send + Debug + PartialEq + Default + 'static + Clone, { self.dispatch().await; let got_state: S = self.get_state().await; - assert_eq!( - discriminant(&got_state), - discriminant(&state), - "State variants are not equal!" - ); + if discriminant(&got_state) != discriminant(&state) { + assert_eqn!(got_state, state, "State variants are not equal!") + } } } diff --git a/teloxide_tests/src/server/messages.rs b/teloxide_tests/src/server/messages.rs new file mode 100644 index 0000000..0b2f148 --- /dev/null +++ b/teloxide_tests/src/server/messages.rs @@ -0,0 +1,248 @@ +use std::collections::HashSet; + +use serde::Serialize; +use teloxide::types::{Message, ReplyMarkup}; + +#[derive(Default)] +pub struct Messages { + pub messages: Vec, + last_message_id: i32, +} + +impl Messages { + pub fn max_message_id(&self) -> i32 { + self.last_message_id + } + + pub fn edit_message(&mut self, message: Message) -> Option { + self.messages.iter().find(|m| m.id == message.id)?; // Find the message (return None if not found) + + self.messages.retain(|m| m.id != message.id); // Remove the old message + self.messages.push(message.clone()); // Add the new message + Some(message) // Profit! + } + + pub fn edit_message_field( + &mut self, + message_id: i32, + field: &str, + value: T, + ) -> Option + where + T: Serialize, + { + let message = self.messages.iter().find(|m| m.id.0 == message_id)?; // Find the message + // (return None if not found) + + let mut json = serde_json::to_value(message).ok()?; // Convert the message to JSON + json[field] = serde_json::to_value(value).ok()?; // Edit the field + let new_message: Message = serde_json::from_value(json).ok()?; // Convert back to Message + + self.messages.retain(|m| m.id.0 != message_id); // Remove the old message + self.messages.push(new_message.clone()); // Add the new message + Some(new_message) // Profit! + } + + pub fn edit_message_reply_markup( + &mut self, + message_id: i32, + reply_markup: Option, + ) -> Option { + match reply_markup { + None => { + // Telegram deletes reply markup when `editMessageText` is called without any. + self.edit_message_field(message_id, "reply_markup", None::<()>) + } + // Only the inline keyboard can be inside of a message + Some(ReplyMarkup::InlineKeyboard(reply_markup)) => { + self.edit_message_field(message_id, "reply_markup", reply_markup) + } + _ => unreachable!("Only InlineKeyboard is allowed"), + } + } + + pub fn add_message(&mut self, message: Message) -> Message { + self.messages.push(message.clone()); + self.last_message_id += 1; + message + } + + pub fn get_message(&self, message_id: i32) -> Option { + self.messages.iter().find(|m| m.id.0 == message_id).cloned() + } + + pub fn delete_message(&mut self, message_id: i32) -> Option { + let message = self + .messages + .iter() + .find(|m| m.id.0 == message_id) + .cloned()?; + self.messages.retain(|m| m.id.0 != message_id); + Some(message) + } + + pub fn delete_messages(&mut self, message_ids: &[i32]) -> Vec { + let message_ids: HashSet = message_ids.iter().cloned().collect(); + let deleted = self + .messages + .iter() + .filter(|m| message_ids.contains(&m.id.0)) + .cloned() + .collect(); + self.messages.retain(|m| !message_ids.contains(&m.id.0)); + deleted + } +} + +#[cfg(test)] +mod tests { + use chrono::{TimeZone, Utc}; + use serial_test::serial; + use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, MessageId}; + + use super::*; + use crate::dataset::*; + + #[test] + #[serial] + fn test_add_messages() { + let mut messages = Messages::default(); + messages.add_message( + message_common::MockMessageText::new() + .text("123") + .id(1) + .build(), + ); + messages.add_message( + message_common::MockMessageText::new() + .text("123") + .id(2) + .build(), + ); + messages.add_message( + message_common::MockMessageText::new() + .text("123") + .id(3) + .build(), + ); + assert_eq!(messages.max_message_id(), 3); + } + + #[test] + #[serial] + fn test_edit_message() { + let mut messages = Messages::default(); + let date = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(); + messages.add_message( + message_common::MockMessageText::new() + .text("123") + .id(1) + .build(), + ); + messages.edit_message( + message_common::MockMessageText::new() + .text("321") + .edit_date(date) + .id(1) + .build(), + ); + let message = messages.get_message(1).unwrap(); + assert_eq!(message.text().unwrap(), "321"); + assert_eq!(message.edit_date().unwrap(), &date); + } + + #[test] + #[serial] + fn test_edit_message_field() { + let mut messages = Messages::default(); + messages.add_message( + message_common::MockMessageText::new() + .text("123") + .id(1) + .build(), + ); + messages.edit_message_field(1, "text", "1234"); + assert_eq!(messages.get_message(1).unwrap().text().unwrap(), "1234"); + } + + #[test] + #[serial] + fn test_get_messages() { + let mut messages = Messages::default(); + messages.add_message( + message_common::MockMessageText::new() + .text("123") + .id(1) + .build(), + ); + assert_eq!(messages.get_message(1).unwrap().text().unwrap(), "123"); + } + + #[test] + #[serial] + fn test_delete_message() { + let mut messages = Messages::default(); + messages.add_message( + message_common::MockMessageText::new() + .text("123") + .id(1) + .build(), + ); + messages.delete_message(1); + assert_eq!(messages.get_message(1), None); + } + + #[test] + #[serial] + fn test_delete_messages() { + let mut messages = Messages::default(); + for id in 1..=5 { + messages.add_message( + message_common::MockMessageText::new() + .text(format!("Message {}", id)) + .id(id) + .build(), + ); + } + + let deleted = messages.delete_messages(&[2, 3]); + + assert_eq!(deleted.len(), 2); + assert_eq!(deleted[0].id, MessageId(2)); + assert_eq!(deleted[1].id, MessageId(3)); + + assert!(messages.get_message(1).is_some()); + assert_eq!(messages.get_message(2), None); + assert_eq!(messages.get_message(3), None); + assert!(messages.get_message(4).is_some()); + assert!(messages.get_message(5).is_some()); + } + + #[test] + #[serial] + fn test_edit_message_reply_markup() { + let mut messages = Messages::default(); + messages.add_message( + message_common::MockMessageText::new() + .text("123") + .id(1) + .build(), + ); + messages.edit_message_reply_markup( + 1, + Some(ReplyMarkup::InlineKeyboard(InlineKeyboardMarkup::new( + vec![vec![InlineKeyboardButton::callback("123", "123")]], + ))), + ); + assert_eq!( + messages + .get_message(1) + .unwrap() + .reply_markup() + .unwrap() + .inline_keyboard[0][0] + .text, + "123" + ); + } +} diff --git a/teloxide_tests/src/server/mod.rs b/teloxide_tests/src/server/mod.rs index 8038118..3bffee3 100644 --- a/teloxide_tests/src/server/mod.rs +++ b/teloxide_tests/src/server/mod.rs @@ -1,620 +1,174 @@ //! A fake telegram bot API for testing purposes. Read more in teloxide_tests crate. -pub mod routes; -use actix_web::{dev::ServerHandle, web, App, HttpResponse, HttpServer, Responder}; -use actix_web_lab::extract::Path; -use lazy_static::lazy_static; +mod routes; +use std::{ + error::Error, + io, + net::TcpListener, + sync::{Arc, Mutex}, +}; + +use actix_web::{ + web::{self, get, post, scope, Data, ServiceConfig}, + App, HttpResponse, HttpServer, Responder, +}; +pub use responses::*; use routes::{ answer_callback_query::*, ban_chat_member::*, copy_message::*, delete_message::*, - download_file::download_file, edit_message_caption::*, edit_message_reply_markup::*, - edit_message_text::*, forward_message::*, get_file::*, pin_chat_message::*, - restrict_chat_member::*, send_animation::*, send_audio::*, send_chat_action::*, - send_contact::*, send_dice::*, send_document::*, send_location::*, send_media_group::*, - send_message::*, send_photo::*, send_poll::*, send_sticker::*, send_venue::*, send_video::*, - send_video_note::*, send_voice::*, unban_chat_member::*, unpin_all_chat_messages::*, - unpin_chat_message::*, set_message_reaction::*, + delete_messages::*, download_file::download_file, edit_message_caption::*, + edit_message_reply_markup::*, edit_message_text::*, forward_message::*, get_file::*, get_me::*, + get_updates::*, get_webhook_info::*, pin_chat_message::*, restrict_chat_member::*, + send_animation::*, send_audio::*, send_chat_action::*, send_contact::*, send_dice::*, + send_document::*, send_invoice::*, send_location::*, send_media_group::*, send_message::*, + send_photo::*, send_poll::*, send_sticker::*, send_venue::*, send_video::*, send_video_note::*, + send_voice::*, set_message_reaction::*, set_my_commands::*, unban_chat_member::*, + unpin_all_chat_messages::*, unpin_chat_message::*, }; -use serde::Serialize; -use std::sync::{ - atomic::{AtomicI32, Ordering}, - Mutex, +pub use routes::{ + copy_message::CopyMessageBody, delete_message::DeleteMessageBody, + delete_messages::DeleteMessagesBody, edit_message_caption::EditMessageCaptionBody, + edit_message_reply_markup::EditMessageReplyMarkupBody, edit_message_text::EditMessageTextBody, + forward_message::ForwardMessageBody, send_animation::SendMessageAnimationBody, + send_audio::SendMessageAudioBody, send_contact::SendMessageContactBody, + send_dice::SendMessageDiceBody, send_document::SendMessageDocumentBody, + send_invoice::SendMessageInvoiceBody, send_location::SendMessageLocationBody, + send_media_group::SendMediaGroupBody, send_message::SendMessageTextBody, + send_photo::SendMessagePhotoBody, send_poll::SendMessagePollBody, + send_sticker::SendMessageStickerBody, send_venue::SendMessageVenueBody, + send_video::SendMessageVideoBody, send_video_note::SendMessageVideoNoteBody, }; -use teloxide::types::{File, Me, Message, MessageId, ReplyMarkup}; - -#[derive(Clone, Debug)] -pub struct SentMessageText { - // For better syntax, this is a struct, not a tuple - pub message: Message, - pub bot_request: SendMessageTextBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessagePhoto { - pub message: Message, - pub bot_request: SendMessagePhotoBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageVideo { - pub message: Message, - pub bot_request: SendMessageVideoBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageAudio { - pub message: Message, - pub bot_request: SendMessageAudioBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageVoice { - pub message: Message, - pub bot_request: SendMessageVoiceBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageVideoNote { - pub message: Message, - pub bot_request: SendMessageVideoNoteBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageDocument { - pub message: Message, - pub bot_request: SendMessageDocumentBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageAnimation { - pub message: Message, - pub bot_request: SendMessageAnimationBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageLocation { - pub message: Message, - pub bot_request: SendMessageLocationBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageVenue { - pub message: Message, - pub bot_request: SendMessageVenueBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageContact { - pub message: Message, - pub bot_request: SendMessageContactBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageDice { - pub message: Message, - pub bot_request: SendMessageDiceBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessagePoll { - pub message: Message, - pub bot_request: SendMessagePollBody, -} - -#[derive(Clone, Debug)] -pub struct SentMessageSticker { - pub message: Message, - pub bot_request: SendMessageStickerBody, -} - -#[derive(Clone, Debug)] -pub struct SentMediaGroup { - pub messages: Vec, - pub bot_request: SendMediaGroupBody, -} - -#[derive(Clone, Debug)] -pub struct EditedMessageText { - pub message: Message, - pub bot_request: EditMessageTextBody, -} - -#[derive(Clone, Debug)] -pub struct EditedMessageCaption { - pub message: Message, - pub bot_request: EditMessageCaptionBody, -} - -#[derive(Clone, Debug)] -pub struct DeletedMessage { - pub message: Message, - pub bot_request: DeleteMessageBody, -} - -#[derive(Clone, Debug)] -pub struct EditedMessageReplyMarkup { - pub message: Message, - pub bot_request: EditMessageReplyMarkupBody, -} - -#[derive(Clone, Debug)] -pub struct ForwardedMessage { - pub message: Message, - pub bot_request: ForwardMessageBody, -} - -#[derive(Clone, Debug)] -pub struct CopiedMessage { - pub message_id: MessageId, - pub bot_request: CopyMessageBody, -} - -#[derive(Clone, Debug)] -pub struct SetMessageReaction { - pub bot_request: SetMessageReactionBody, -} - -#[derive(Clone, Debug, Default)] -pub struct Responses { - /// All of the sent messages, including text, photo, audio, etc. - /// Be warned, editing or deleting messages do not affect this list! - pub sent_messages: Vec, - - /// This has only messages that are text messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_text: Vec, - - /// This has only messages that are photo messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_photo: Vec, - - /// This has only messages that are video messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_video: Vec, - - /// This has only messages that are audio messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_audio: Vec, - - /// This has only messages that are voice messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_voice: Vec, - - /// This has only messages that are video note messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_video_note: Vec, - - /// This has only messages that are document messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_document: Vec, - - /// This has only messages that are animation messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_animation: Vec, - - /// This has only messages that are location messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_location: Vec, - - /// This has only messages that are venue messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_venue: Vec, - - /// This has only messages that are contact messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_contact: Vec, - - /// This has only messages that are dice messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_dice: Vec, - - /// This has only messages that are poll messages, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_poll: Vec, - - /// This has only messages that are stickers, sent by the bot. - /// The `.message` field has the sent by bot message, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_messages_sticker: Vec, - - /// This has only messages that are media group messages, sent by the bot. - /// The `.messages` field has the sent by bot messages, and `.bot_request` - /// has the request that was sent to the fake server - pub sent_media_group: Vec, - - /// This has only edited by the bot text messages. - /// The `.message` field has the new edited message, and `.bot_request` - /// has the request that was sent to the fake server - pub edited_messages_text: Vec, - - /// This has only edited by the bot caption messages. - /// The `.message` field has the new edited message, and `.bot_request` - /// has the request that was sent to the fake server - pub edited_messages_caption: Vec, - - /// This has only messages whos reply markup was edited by the bot. - /// The `.message` field has the new edited message, and `.bot_request` - /// has the request that was sent to the fake server - pub edited_messages_reply_markup: Vec, - - /// This has only messages which were deleted by the bot. - /// The `.message` field has the deleted message, and `.bot_request` - /// has the request that was sent to the fake server - pub deleted_messages: Vec, - - /// This has only the requests that were sent to the fake server to forward messages. - /// The `.message` field has the forwarded message, and `.bot_request` - /// has the request that was sent to the fake server - pub forwarded_messages: Vec, - - /// This has only the requests that were sent to the fake server to copy messages. - /// The `.message_id` field has the copied message id, and `.bot_request` - /// has the request that was sent to the fake server - pub copied_messages: Vec, - - /// This has only the requests that were sent to the fake server to answer callback queries. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub answered_callback_queries: Vec, - - /// This has only the requests that were sent to the fake server to pin messages. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub pinned_chat_messages: Vec, - - /// This has only the requests that were sent to the fake server to unpin messages. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub unpinned_chat_messages: Vec, - - /// This has only the requests that were sent to the fake server to unpin all messages. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub unpinned_all_chat_messages: Vec, - - /// This has only the requests that were sent to the fake server to ban chat members. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub banned_chat_members: Vec, - - /// This has only the requests that were sent to the fake server to unban chat members. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub unbanned_chat_members: Vec, - - /// This has only the requests that were sent to the fake server to restrict chat members. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub restricted_chat_members: Vec, - - /// This has only the requests that were sent to the fake server to send chat actions. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub sent_chat_actions: Vec, - - /// This has only the requests that were sent to the fake server to set message reactions. - /// Telegram doesn't return anything, because there isn't anything to return, so there is no - /// `.message` field. - pub set_message_reaction: Vec, -} - -lazy_static! { - pub static ref MESSAGES: Mutex> = Mutex::new(vec![]); // Messages storage, just in case - pub static ref FILES: Mutex> = Mutex::new(vec![]); // Messages storage, just in case - pub static ref RESPONSES: Mutex = Mutex::new(Responses::default()); // - pub static ref LAST_MESSAGE_ID: AtomicI32 = AtomicI32::new(0); -} - -impl MESSAGES { - pub fn max_message_id(&self) -> i32 { - LAST_MESSAGE_ID.load(Ordering::Relaxed) - } - - pub fn edit_message(&self, message_id: i32, field: &str, value: T) -> Option - where - T: Serialize, - { - let mut messages = self.lock().unwrap(); // Get the message lock - let message = messages.iter().find(|m| m.id.0 == message_id)?; // Find the message - // (return None if not found) - - let mut json = serde_json::to_value(&message).ok()?; // Convert the message to JSON - json[field] = serde_json::to_value(value).ok()?; // Edit the field - let new_message: Message = serde_json::from_value(json).ok()?; // Convert back to Message - - messages.retain(|m| m.id.0 != message_id); // Remove the old message - messages.push(new_message.clone()); // Add the new message - Some(new_message) // Profit! - } - - pub fn edit_message_reply_markup( - &self, - message_id: i32, - reply_markup: Option, - ) -> Option { - match reply_markup { - // Only the inline keyboard can be inside of a message - Some(ReplyMarkup::InlineKeyboard(reply_markup)) => { - MESSAGES.edit_message(message_id, "reply_markup", reply_markup) - } - _ => MESSAGES.get_message(message_id), - } - } - - pub fn add_message(&self, message: Message) -> Message { - self.lock().unwrap().push(message.clone()); - LAST_MESSAGE_ID.fetch_add(1, Ordering::Relaxed); - message - } - - pub fn get_message(&self, message_id: i32) -> Option { - self.lock() - .unwrap() - .iter() - .find(|m| m.id.0 == message_id) - .cloned() - } - - pub fn delete_message(&self, message_id: i32) -> Option { - let mut messages = self.lock().unwrap(); - let message = messages.iter().find(|m| m.id.0 == message_id).cloned()?; - messages.retain(|m| m.id.0 != message_id); - Some(message) - } -} - -pub async fn ping() -> impl Responder { - "pong" -} - -#[allow(dead_code)] -pub async fn log_request(body: web::Json) -> impl Responder { - dbg!(body); - HttpResponse::Ok() -} - -#[derive(Default)] -struct StopHandle { - inner: parking_lot::Mutex>, -} - -impl StopHandle { - /// Sets the server handle to stop. - pub(crate) fn register(&self, handle: ServerHandle) { - *self.inner.lock() = Some(handle); - } - - /// Sends stop signal through contained server handle. - pub(crate) fn stop(&self, graceful: bool) { - #[allow(clippy::let_underscore_future)] - let _ = self.inner.lock().as_ref().unwrap().stop(graceful); - } -} - -async fn stop(Path(graceful): Path, stop_handle: web::Data) -> HttpResponse { - stop_handle.stop(graceful); - HttpResponse::NoContent().finish() -} - -pub async fn main(port: Mutex, me: Me) { - // MESSAGES don't care if they are cleaned or not - *RESPONSES.lock().unwrap() = Responses::default(); - - let pong = reqwest::get(format!("http://127.0.0.1:{}/ping", port.lock().unwrap())).await; - - if pong.is_err() - // If it errored, no server is running, we need to start it - { - let stop_handle = web::Data::new(StopHandle::default()); - // let _ = env_logger::builder() - // .filter_level(log::LevelFilter::Info) - // .format_target(false) - // .format_timestamp(None) - // .try_init(); - let server = HttpServer::new({ - let stop_handle = stop_handle.clone(); - - move || { - App::new() - // .wrap(actix_web::middleware::Logger::default()) - .app_data(stop_handle.clone()) - .app_data(web::Data::new(me.clone())) - .route("/ping", web::get().to(ping)) - .route("/stop/{graceful}", web::post().to(stop)) - .route("/bot{token}/GetFile", web::post().to(get_file)) - .route("/bot{token}/SendMessage", web::post().to(send_message)) - .route("/bot{token}/SendPhoto", web::post().to(send_photo)) - .route("/bot{token}/SendVideo", web::post().to(send_video)) - .route("/bot{token}/SendVoice", web::post().to(send_voice)) - .route("/bot{token}/SendAudio", web::post().to(send_audio)) - .route("/bot{token}/SendVideoNote", web::post().to(send_video_note)) - .route("/bot{token}/SendDocument", web::post().to(send_document)) - .route("/bot{token}/SendAnimation", web::post().to(send_animation)) - .route("/bot{token}/SendLocation", web::post().to(send_location)) - .route("/bot{token}/SendVenue", web::post().to(send_venue)) - .route("/bot{token}/SendContact", web::post().to(send_contact)) - .route("/bot{token}/SendSticker", web::post().to(send_sticker)) - .route( - "/bot{token}/SendChatAction", - web::post().to(send_chat_action), - ) - .route("/bot{token}/SendDice", web::post().to(send_dice)) - .route("/bot{token}/SendPoll", web::post().to(send_poll)) - .route( - "/bot{token}/SendMediaGroup", - web::post().to(send_media_group), - ) - .route( - "/bot{token}/EditMessageText", - web::post().to(edit_message_text), - ) - .route( - "/bot{token}/EditMessageCaption", - web::post().to(edit_message_caption), - ) - .route( - "/bot{token}/EditMessageReplyMarkup", - web::post().to(edit_message_reply_markup), - ) - .route("/bot{token}/DeleteMessage", web::post().to(delete_message)) - .route( - "/bot{token}/ForwardMessage", - web::post().to(forward_message), - ) - .route("/bot{token}/CopyMessage", web::post().to(copy_message)) - .route( - "/bot{token}/AnswerCallbackQuery", - web::post().to(answer_callback_query), - ) - .route( - "/bot{token}/PinChatMessage", - web::post().to(pin_chat_message), - ) - .route( - "/bot{token}/UnpinChatMessage", - web::post().to(unpin_chat_message), - ) - .route( - "/bot{token}/UnpinAllChatMessages", - web::post().to(unpin_all_chat_messages), - ) - .route("/bot{token}/BanChatMember", web::post().to(ban_chat_member)) - .route( - "/bot{token}/UnbanChatMember", - web::post().to(unban_chat_member), - ) - .route( - "/bot{token}/RestrictChatMember", - web::post().to(restrict_chat_member), - ) - .route( - "/bot{token}/SetMessageReaction", - web::post().to(set_message_reaction), - ) - .route("/file/bot{token}/{file_name}", web::get().to(download_file)) - } +use teloxide::types::Me; +use tokio::{ + sync::mpsc::{channel, Sender}, + task::{JoinError, JoinHandle}, +}; +use tokio_util::sync::CancellationToken; + +use crate::state::State; + +pub mod messages; +pub mod responses; + +pub(crate) struct ServerManager { + pub port: u16, + server: JoinHandle<()>, + cancel_token: CancellationToken, +} + +#[warn(clippy::unwrap_used)] +impl ServerManager { + pub(crate) async fn start(me: Me, state: Arc>) -> Result> { + let listener = TcpListener::bind("127.0.0.1:0")?; + let port = listener.local_addr()?.port(); + + let cancel_token = CancellationToken::new(); + let (tx, mut rx) = channel::<()>(100); + + let server = tokio::spawn(run_server( + listener, + me, + state.clone(), + cancel_token.clone(), + tx, + )); + // Waits until the server is ready + rx.recv().await; + + Ok(Self { + port, + cancel_token, + server, }) - .bind(format!("127.0.0.1:{}", port.lock().unwrap().to_string())) - .unwrap() - .workers(1) - .run(); - - stop_handle.register(server.handle()); - - server.await.unwrap(); - }; -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::dataset::*; - use serial_test::serial; - use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup}; - - #[test] - #[serial] - fn test_add_messages() { - MESSAGES.lock().unwrap().clear(); - LAST_MESSAGE_ID.store(0, Ordering::Relaxed); - MESSAGES.add_message( - message_common::MockMessageText::new() - .text("123") - .id(1) - .build(), - ); - MESSAGES.add_message( - message_common::MockMessageText::new() - .text("123") - .id(2) - .build(), - ); - MESSAGES.add_message( - message_common::MockMessageText::new() - .text("123") - .id(3) - .build(), - ); - assert_eq!(MESSAGES.max_message_id(), 3); - } - - #[test] - #[serial] - fn test_edit_messages() { - MESSAGES.lock().unwrap().clear(); - MESSAGES.add_message( - message_common::MockMessageText::new() - .text("123") - .id(1) - .build(), - ); - MESSAGES.edit_message(1, "text", "1234"); - assert_eq!(MESSAGES.get_message(1).unwrap().text().unwrap(), "1234"); } - #[test] - #[serial] - fn test_get_messages() { - MESSAGES.lock().unwrap().clear(); - MESSAGES.add_message( - message_common::MockMessageText::new() - .text("123") - .id(1) - .build(), - ); - assert_eq!(MESSAGES.get_message(1).unwrap().text().unwrap(), "123"); - } - - #[test] - #[serial] - fn test_delete_messages() { - MESSAGES.lock().unwrap().clear(); - MESSAGES.add_message( - message_common::MockMessageText::new() - .text("123") - .id(1) - .build(), - ); - MESSAGES.delete_message(1); - assert_eq!(MESSAGES.get_message(1), None); + pub(crate) async fn stop(self) -> Result<(), JoinError> { + self.cancel_token.cancel(); + self.server.await } +} - #[test] - #[serial] - fn test_edit_message_reply_markup() { - MESSAGES.lock().unwrap().clear(); - MESSAGES.add_message( - message_common::MockMessageText::new() - .text("123") - .id(1) - .build(), - ); - MESSAGES.edit_message_reply_markup( - 1, - Some(ReplyMarkup::InlineKeyboard(InlineKeyboardMarkup::new( - vec![vec![InlineKeyboardButton::callback("123", "123")]], - ))), - ); - assert_eq!( - MESSAGES - .get_message(1) - .unwrap() - .reply_markup() - .unwrap() - .inline_keyboard[0][0] - .text, - "123" - ); - } +async fn run_server( + listener: TcpListener, + me: Me, + state: Arc>, + cancel_token: CancellationToken, + tx: Sender<()>, +) { + let server = create_server(listener, me, state).unwrap(); + tx.send(()).await.unwrap(); + let server_handle = server.handle(); + + tokio::spawn(async move { + cancel_token.cancelled().await; + server_handle.stop(false).await; + }); + + server.await.unwrap(); +} + +fn create_server( + listener: TcpListener, + me: Me, + state: Arc>, +) -> io::Result { + Ok(HttpServer::new(move || { + App::new() + .app_data(Data::new(me.clone())) + .app_data(Data::from(state.clone())) + .configure(set_routes) + }) + .listen(listener)? + .run()) +} + +fn set_routes(cfg: &mut ServiceConfig) { + cfg.route("/file/bot{token}/{file_name}", get().to(download_file)) + .service(scope("/bot{token}").configure(set_bot_routes)); +} + +fn set_bot_routes(cfg: &mut ServiceConfig) { + cfg.route("/GetFile", post().to(get_file)) + .route("/SendMessage", post().to(send_message)) + .route("/GetWebhookInfo", post().to(get_webhook_info)) + .route("/GetMe", post().to(get_me)) + .route("/GetUpdates", post().to(get_updates)) + .route("/SendPhoto", post().to(send_photo)) + .route("/SendVideo", post().to(send_video)) + .route("/SendVoice", post().to(send_voice)) + .route("/SendAudio", post().to(send_audio)) + .route("/SendVideoNote", post().to(send_video_note)) + .route("/SendDocument", post().to(send_document)) + .route("/SendAnimation", post().to(send_animation)) + .route("/SendLocation", post().to(send_location)) + .route("/SendVenue", post().to(send_venue)) + .route("/SendContact", post().to(send_contact)) + .route("/SendSticker", post().to(send_sticker)) + .route("/SendChatAction", post().to(send_chat_action)) + .route("/SendDice", post().to(send_dice)) + .route("/SendPoll", post().to(send_poll)) + .route("/SendMediaGroup", post().to(send_media_group)) + .route("/SendInvoice", post().to(send_invoice)) + .route("/EditMessageText", post().to(edit_message_text)) + .route("/EditMessageCaption", post().to(edit_message_caption)) + .route( + "/EditMessageReplyMarkup", + post().to(edit_message_reply_markup), + ) + .route("/DeleteMessage", post().to(delete_message)) + .route("/DeleteMessages", post().to(delete_messages)) + .route("/ForwardMessage", post().to(forward_message)) + .route("/CopyMessage", post().to(copy_message)) + .route("/AnswerCallbackQuery", post().to(answer_callback_query)) + .route("/PinChatMessage", post().to(pin_chat_message)) + .route("/UnpinChatMessage", post().to(unpin_chat_message)) + .route("/UnpinAllChatMessages", post().to(unpin_all_chat_messages)) + .route("/BanChatMember", post().to(ban_chat_member)) + .route("/UnbanChatMember", post().to(unban_chat_member)) + .route("/RestrictChatMember", post().to(restrict_chat_member)) + .route("/SetMessageReaction", post().to(set_message_reaction)) + .route("/SetMyCommands", post().to(set_my_commands)) + .route("/{unknown_endpoint}", post().to(unknown_endpoint)); +} + +async fn unknown_endpoint(path: web::Path<(String, String)>) -> impl Responder { + HttpResponse::InternalServerError().message_body(format!("Endpoint \"{}\" is not yet implemented! Please make an issue to https://github.com/LasterAlex/teloxide_tests/issues/new?assignees=&labels=no+endpoint&projects=&template=add-endpoint-template.md&title=", path.1)) } diff --git a/teloxide_tests/src/server/responses.rs b/teloxide_tests/src/server/responses.rs new file mode 100644 index 0000000..fd95a58 --- /dev/null +++ b/teloxide_tests/src/server/responses.rs @@ -0,0 +1,312 @@ +use teloxide::types::{Message, MessageId}; + +use super::routes::{ + answer_callback_query::*, ban_chat_member::*, copy_message::*, delete_message::*, + edit_message_caption::*, edit_message_reply_markup::*, edit_message_text::*, + forward_message::*, pin_chat_message::*, restrict_chat_member::*, send_animation::*, + send_audio::*, send_chat_action::*, send_contact::*, send_dice::*, send_document::*, + send_invoice::*, send_location::*, send_media_group::*, send_message::*, send_photo::*, + send_poll::*, send_sticker::*, send_venue::*, send_video::*, send_video_note::*, send_voice::*, + set_message_reaction::*, set_my_commands::*, unban_chat_member::*, unpin_all_chat_messages::*, + unpin_chat_message::*, +}; + +#[derive(Clone, Debug)] +pub struct SentMessageText { + // For better syntax, this is a struct, not a tuple + pub message: Message, + pub bot_request: SendMessageTextBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessagePhoto { + pub message: Message, + pub bot_request: SendMessagePhotoBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageVideo { + pub message: Message, + pub bot_request: SendMessageVideoBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageAudio { + pub message: Message, + pub bot_request: SendMessageAudioBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageVoice { + pub message: Message, + pub bot_request: SendMessageVoiceBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageVideoNote { + pub message: Message, + pub bot_request: SendMessageVideoNoteBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageDocument { + pub message: Message, + pub bot_request: SendMessageDocumentBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageAnimation { + pub message: Message, + pub bot_request: SendMessageAnimationBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageLocation { + pub message: Message, + pub bot_request: SendMessageLocationBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageVenue { + pub message: Message, + pub bot_request: SendMessageVenueBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageContact { + pub message: Message, + pub bot_request: SendMessageContactBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageDice { + pub message: Message, + pub bot_request: SendMessageDiceBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessagePoll { + pub message: Message, + pub bot_request: SendMessagePollBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageSticker { + pub message: Message, + pub bot_request: SendMessageStickerBody, +} + +#[derive(Clone, Debug)] +pub struct SentMediaGroup { + pub messages: Vec, + pub bot_request: SendMediaGroupBody, +} + +#[derive(Clone, Debug)] +pub struct SentMessageInvoice { + pub message: Message, + pub bot_request: SendMessageInvoiceBody, +} + +#[derive(Clone, Debug)] +pub struct EditedMessageText { + pub message: Message, + pub bot_request: EditMessageTextBody, +} + +#[derive(Clone, Debug)] +pub struct EditedMessageCaption { + pub message: Message, + pub bot_request: EditMessageCaptionBody, +} + +#[derive(Clone, Debug)] +pub struct DeletedMessage { + pub message: Message, + pub bot_request: DeleteMessageBody, +} + +#[derive(Clone, Debug)] +pub struct EditedMessageReplyMarkup { + pub message: Message, + pub bot_request: EditMessageReplyMarkupBody, +} + +#[derive(Clone, Debug)] +pub struct ForwardedMessage { + pub message: Message, + pub bot_request: ForwardMessageBody, +} + +#[derive(Clone, Debug)] +pub struct CopiedMessage { + pub message_id: MessageId, + pub bot_request: CopyMessageBody, +} + +#[derive(Clone, Debug, Default)] +pub struct Responses { + /// All of the sent messages, including text, photo, audio, etc. + /// Be warned, editing or deleting messages do not affect this list! + pub sent_messages: Vec, + + /// This has only messages that are text messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_text: Vec, + + /// This has only messages that are photo messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_photo: Vec, + + /// This has only messages that are video messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_video: Vec, + + /// This has only messages that are audio messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_audio: Vec, + + /// This has only messages that are voice messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_voice: Vec, + + /// This has only messages that are video note messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_video_note: Vec, + + /// This has only messages that are document messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_document: Vec, + + /// This has only messages that are animation messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_animation: Vec, + + /// This has only messages that are location messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_location: Vec, + + /// This has only messages that are venue messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_venue: Vec, + + /// This has only messages that are contact messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_contact: Vec, + + /// This has only messages that are dice messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_dice: Vec, + + /// This has only messages that are poll messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_poll: Vec, + + /// This has only messages that are stickers, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_sticker: Vec, + + /// This has only messages that are media group messages, sent by the bot. + /// The `.messages` field has the sent by bot messages, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_media_group: Vec, + + /// This has only messages that are invoice messages, sent by the bot. + /// The `.message` field has the sent by bot message, and `.bot_request` + /// has the request that was sent to the fake server + pub sent_messages_invoice: Vec, + + /// This has only edited by the bot text messages. + /// The `.message` field has the new edited message, and `.bot_request` + /// has the request that was sent to the fake server + pub edited_messages_text: Vec, + + /// This has only edited by the bot caption messages. + /// The `.message` field has the new edited message, and `.bot_request` + /// has the request that was sent to the fake server + pub edited_messages_caption: Vec, + + /// This has only messages whos reply markup was edited by the bot. + /// The `.message` field has the new edited message, and `.bot_request` + /// has the request that was sent to the fake server + pub edited_messages_reply_markup: Vec, + + /// This has only messages which were deleted by the bot. + /// The `.message` field has the deleted message, and `.bot_request` + /// has the request that was sent to the fake server + pub deleted_messages: Vec, + + /// This has only the requests that were sent to the fake server to forward messages. + /// The `.message` field has the forwarded message, and `.bot_request` + /// has the request that was sent to the fake server + pub forwarded_messages: Vec, + + /// This has only the requests that were sent to the fake server to copy messages. + /// The `.message_id` field has the copied message id, and `.bot_request` + /// has the request that was sent to the fake server + pub copied_messages: Vec, + + /// This has only the requests that were sent to the fake server to answer callback queries. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub answered_callback_queries: Vec, + + /// This has only the requests that were sent to the fake server to pin messages. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub pinned_chat_messages: Vec, + + /// This has only the requests that were sent to the fake server to unpin messages. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub unpinned_chat_messages: Vec, + + /// This has only the requests that were sent to the fake server to unpin all messages. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub unpinned_all_chat_messages: Vec, + + /// This has only the requests that were sent to the fake server to ban chat members. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub banned_chat_members: Vec, + + /// This has only the requests that were sent to the fake server to unban chat members. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub unbanned_chat_members: Vec, + + /// This has only the requests that were sent to the fake server to restrict chat members. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub restricted_chat_members: Vec, + + /// This has only the requests that were sent to the fake server to send chat actions. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub sent_chat_actions: Vec, + + /// This has only the requests that were sent to the fake server to set message reactions. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub set_message_reaction: Vec, + + /// This has only the requests that were sent to the fake server to set message reactions. + /// Telegram doesn't return anything, because there isn't anything to return, so there is no + /// `.message` field. + pub set_my_commands: Vec, +} diff --git a/teloxide_tests/src/server/routes/answer_callback_query.rs b/teloxide_tests/src/server/routes/answer_callback_query.rs index b37b297..bfc1a6d 100644 --- a/teloxide_tests/src/server/routes/answer_callback_query.rs +++ b/teloxide_tests/src/server/routes/answer_callback_query.rs @@ -1,9 +1,10 @@ +use std::sync::Mutex; + use actix_web::{web, Responder}; use serde::Deserialize; -use crate::server::RESPONSES; - use super::make_telegram_result; +use crate::state::State; #[derive(Debug, Deserialize, Clone)] pub struct AnswerCallbackQueryBody { @@ -14,9 +15,12 @@ pub struct AnswerCallbackQueryBody { pub cache_time: Option, } -pub async fn answer_callback_query(body: web::Json) -> impl Responder { - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock +pub async fn answer_callback_query( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); + lock.responses .answered_callback_queries .push(body.into_inner()); make_telegram_result(true) diff --git a/teloxide_tests/src/server/routes/ban_chat_member.rs b/teloxide_tests/src/server/routes/ban_chat_member.rs index 9a4e08e..2c633f5 100644 --- a/teloxide_tests/src/server/routes/ban_chat_member.rs +++ b/teloxide_tests/src/server/routes/ban_chat_member.rs @@ -1,10 +1,10 @@ +use std::sync::Mutex; + use actix_web::{web, Responder}; use serde::Deserialize; -use crate::server::routes::make_telegram_result; -use crate::server::{MESSAGES, RESPONSES}; - use super::BodyChatId; +use crate::{server::routes::make_telegram_result, state::State}; #[derive(Debug, Deserialize, Clone)] pub struct BanChatMemberBody { @@ -14,21 +14,23 @@ pub struct BanChatMemberBody { pub revoke_messages: Option, } -pub async fn ban_chat_member(body: web::Json) -> impl Responder { +pub async fn ban_chat_member( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); let chat_id = body.chat_id.id(); - let messages = MESSAGES.lock().unwrap().clone(); if body.revoke_messages.is_some() && body.revoke_messages.unwrap() { - for message in messages { + for message in lock.messages.messages.clone() { if message.chat.id.0 == chat_id && message.from.is_some() && message.from.unwrap().id.0 == body.user_id { - MESSAGES.delete_message(message.id.0); + lock.messages.delete_message(message.id.0); } } } - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.banned_chat_members.push(body.into_inner()); + lock.responses.banned_chat_members.push(body.into_inner()); make_telegram_result(true) } diff --git a/teloxide_tests/src/server/routes/copy_message.rs b/teloxide_tests/src/server/routes/copy_message.rs index 36e88b5..fc97efd 100644 --- a/teloxide_tests/src/server/routes/copy_message.rs +++ b/teloxide_tests/src/server/routes/copy_message.rs @@ -1,5 +1,6 @@ -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; +use std::sync::Mutex; + +use actix_web::{error::ErrorBadRequest, web, Responder}; use serde::Deserialize; use serde_json::json; use teloxide::types::{ @@ -7,10 +8,11 @@ use teloxide::types::{ MessageEntity, MessageId, MessageKind, ParseMode, ReplyMarkup, }; -use crate::server::CopiedMessage; -use crate::server::{routes::check_if_message_exists, MESSAGES, RESPONSES}; - use super::{make_telegram_result, BodyChatId}; +use crate::{ + server::{routes::check_if_message_exists, CopiedMessage}, + state::State, +}; #[derive(Debug, Deserialize, Clone)] pub struct CopyMessageBody { @@ -24,17 +26,22 @@ pub struct CopyMessageBody { pub show_caption_above_media: Option, pub disable_notification: Option, pub protect_content: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, } -pub async fn copy_message(body: web::Json, me: web::Data) -> impl Responder { +pub async fn copy_message( + body: web::Json, + me: web::Data, + state: web::Data>, +) -> impl Responder { + let mut lock = state.lock().unwrap(); let chat = body.chat_id.chat(); - check_if_message_exists!(body.message_id); - let mut message = MESSAGES.get_message(body.message_id).unwrap(); + check_if_message_exists!(lock, body.message_id); + let mut message = lock.messages.get_message(body.message_id).unwrap(); message.chat = chat; message.from = Some(me.user.clone()); + // FIXME: Use show_caption_above_media if let MessageKind::Common(ref mut common) = message.kind { common.forward_origin = None; common.external_reply = None; @@ -80,14 +87,13 @@ pub async fn copy_message(body: web::Json, me: web::Data) - common.has_protected_content = body.protect_content.unwrap_or(false); } - let last_id = MESSAGES.max_message_id(); + let last_id = lock.messages.max_message_id(); message.id = MessageId(last_id + 1); message.chat = body.chat_id.chat(); - let message = MESSAGES.add_message(message); + let message = lock.messages.add_message(message); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.copied_messages.push(CopiedMessage { + lock.responses.sent_messages.push(message.clone()); + lock.responses.copied_messages.push(CopiedMessage { message_id: message.id, bot_request: body.into_inner(), }); diff --git a/teloxide_tests/src/server/routes/delete_message.rs b/teloxide_tests/src/server/routes/delete_message.rs index 3fe6563..69a045a 100644 --- a/teloxide_tests/src/server/routes/delete_message.rs +++ b/teloxide_tests/src/server/routes/delete_message.rs @@ -1,11 +1,13 @@ -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; +use std::sync::Mutex; -use crate::server::routes::make_telegram_result; -use crate::server::{DeletedMessage, MESSAGES, RESPONSES}; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; use super::{check_if_message_exists, BodyChatId}; +use crate::{ + server::{routes::make_telegram_result, DeletedMessage}, + state::State, +}; #[derive(Debug, Deserialize, Clone)] pub struct DeleteMessageBody { @@ -13,11 +15,14 @@ pub struct DeleteMessageBody { pub message_id: i32, } -pub async fn delete_message(body: web::Json) -> impl Responder { - check_if_message_exists!(body.message_id); - let deleted_message = MESSAGES.delete_message(body.message_id).unwrap(); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.deleted_messages.push(DeletedMessage { +pub async fn delete_message( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); + check_if_message_exists!(lock, body.message_id); + let deleted_message = lock.messages.delete_message(body.message_id).unwrap(); + lock.responses.deleted_messages.push(DeletedMessage { message: deleted_message.clone(), bot_request: body.into_inner(), }); diff --git a/teloxide_tests/src/server/routes/delete_messages.rs b/teloxide_tests/src/server/routes/delete_messages.rs new file mode 100644 index 0000000..b5f80ab --- /dev/null +++ b/teloxide_tests/src/server/routes/delete_messages.rs @@ -0,0 +1,46 @@ +use std::sync::Mutex; + +use actix_web::{web, Responder}; +use serde::Deserialize; + +use super::BodyChatId; +use crate::{ + server::{ + routes::{delete_message::DeleteMessageBody, make_telegram_result}, + DeletedMessage, + }, + state::State, +}; + +#[derive(Debug, Deserialize, Clone)] +pub struct DeleteMessagesBody { + pub chat_id: BodyChatId, + pub message_ids: Vec, +} + +pub async fn delete_messages( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); + let bot_request = body.into_inner(); + // deleteMessages skips messages that are not found, no error is returned. + let mut deleted_messages = lock + .messages + .delete_messages(&bot_request.message_ids) + .into_iter() + .map(|m| DeletedMessage { + message: m.clone(), + bot_request: DeleteMessageBody { + chat_id: bot_request.chat_id.clone(), + message_id: m.id.0, + }, + }) + .collect(); + + lock.responses + .deleted_messages + .append(&mut deleted_messages); + + make_telegram_result(true) +} diff --git a/teloxide_tests/src/server/routes/download_file.rs b/teloxide_tests/src/server/routes/download_file.rs index 0fc890c..004a9fe 100644 --- a/teloxide_tests/src/server/routes/download_file.rs +++ b/teloxide_tests/src/server/routes/download_file.rs @@ -1,4 +1,4 @@ -use std::fmt::Error; +use std::{fmt::Error, sync::Mutex}; use actix_web::{ error::ErrorBadRequest, @@ -7,12 +7,16 @@ use actix_web::{ }; use futures_util::{future::ok, stream::once}; -use crate::server::FILES; +use crate::state::State; -pub async fn download_file(path: web::Path<(String, String)>) -> HttpResponse { - if FILES +pub async fn download_file( + path: web::Path<(String, String)>, + state: web::Data>, +) -> HttpResponse { + if state .lock() .unwrap() + .files .clone() .into_iter() .find(|f| f.path == path.1) diff --git a/teloxide_tests/src/server/routes/edit_message_caption.rs b/teloxide_tests/src/server/routes/edit_message_caption.rs index eb686d2..2f53299 100644 --- a/teloxide_tests/src/server/routes/edit_message_caption.rs +++ b/teloxide_tests/src/server/routes/edit_message_caption.rs @@ -1,12 +1,14 @@ -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; -use teloxide::types::{MessageEntity, ParseMode, ReplyMarkup}; +use std::sync::Mutex; -use crate::server::routes::make_telegram_result; -use crate::server::{EditedMessageCaption, MESSAGES, RESPONSES}; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; +use teloxide::types::{BusinessConnectionId, MessageEntity, ParseMode, ReplyMarkup}; use super::{check_if_message_exists, BodyChatId}; +use crate::{ + server::{routes::make_telegram_result, EditedMessageCaption}, + state::State, +}; #[derive(Debug, Deserialize, Clone)] pub struct EditMessageCaptionBody { @@ -17,31 +19,41 @@ pub struct EditMessageCaptionBody { pub parse_mode: Option, pub caption_entities: Option>, pub show_caption_above_media: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, + pub business_connection_id: Option, } -pub async fn edit_message_caption(body: web::Json) -> impl Responder { +pub async fn edit_message_caption( + state: web::Data>, + body: web::Json, +) -> impl Responder { match ( body.chat_id.clone(), body.message_id, body.inline_message_id.clone(), ) { (Some(_), Some(message_id), None) => { - check_if_message_exists!(message_id); - MESSAGES.edit_message(message_id, "caption", body.caption.clone()); - MESSAGES.edit_message( + let mut lock = state.lock().unwrap(); + check_if_message_exists!(lock, message_id); + lock.messages + .edit_message_field(message_id, "caption", body.caption.clone()); + lock.messages.edit_message_field( message_id, "caption_entities", body.caption_entities.clone().unwrap_or_default(), ); + lock.messages.edit_message_field( + message_id, + "show_caption_above_media", + body.show_caption_above_media.unwrap_or(false), + ); - let message = MESSAGES + let message = lock + .messages .edit_message_reply_markup(message_id, body.reply_markup.clone()) .unwrap(); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock + lock.responses .edited_messages_caption .push(EditedMessageCaption { message: message.clone(), diff --git a/teloxide_tests/src/server/routes/edit_message_reply_markup.rs b/teloxide_tests/src/server/routes/edit_message_reply_markup.rs index 5cbc0b4..83538fa 100644 --- a/teloxide_tests/src/server/routes/edit_message_reply_markup.rs +++ b/teloxide_tests/src/server/routes/edit_message_reply_markup.rs @@ -1,24 +1,30 @@ -use crate::server::routes::{check_if_message_exists, make_telegram_result}; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; -use teloxide::types::ReplyMarkup; +use std::sync::Mutex; -use crate::server::{EditedMessageReplyMarkup, MESSAGES, RESPONSES}; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; +use teloxide::types::{BusinessConnectionId, ReplyMarkup}; use super::BodyChatId; +use crate::{ + server::{ + routes::{check_if_message_exists, make_telegram_result}, + EditedMessageReplyMarkup, + }, + state::State, +}; #[derive(Debug, Deserialize, Clone)] pub struct EditMessageReplyMarkupBody { pub chat_id: Option, pub message_id: Option, pub inline_message_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, + pub business_connection_id: Option, } pub async fn edit_message_reply_markup( body: web::Json, + state: web::Data>, ) -> impl Responder { match ( body.chat_id.clone(), @@ -26,19 +32,21 @@ pub async fn edit_message_reply_markup( body.inline_message_id.clone(), ) { (Some(_), Some(message_id), None) => { - check_if_message_exists!(message_id); + let mut lock = state.lock().unwrap(); + check_if_message_exists!(lock, message_id); let message = match body.reply_markup.clone() { - Some(reply_markup) => MESSAGES - .edit_message(message_id, "reply_markup", reply_markup) + Some(reply_markup) => lock + .messages + .edit_message_field(message_id, "reply_markup", reply_markup) .unwrap(), - None => MESSAGES - .edit_message(message_id, "reply_markup", None::<()>) + None => lock + .messages + .edit_message_field(message_id, "reply_markup", None::<()>) .unwrap(), }; - let mut response_lock = RESPONSES.lock().unwrap(); - response_lock + lock.responses .edited_messages_reply_markup .push(EditedMessageReplyMarkup { message: message.clone(), diff --git a/teloxide_tests/src/server/routes/edit_message_text.rs b/teloxide_tests/src/server/routes/edit_message_text.rs index 5fd86d4..cf03f20 100644 --- a/teloxide_tests/src/server/routes/edit_message_text.rs +++ b/teloxide_tests/src/server/routes/edit_message_text.rs @@ -1,10 +1,17 @@ -use actix_web::{error::ErrorBadRequest, web, Responder}; -use serde::Deserialize; -use teloxide::types::{LinkPreviewOptions, MessageEntity, ParseMode, ReplyMarkup}; +use std::sync::Mutex; -use crate::server::{routes::make_telegram_result, EditedMessageText, MESSAGES, RESPONSES}; +use actix_web::{error::ErrorBadRequest, web, Responder, ResponseError}; +use serde::Deserialize; +use teloxide::{ + types::{BusinessConnectionId, LinkPreviewOptions, MessageEntity, ParseMode, ReplyMarkup}, + ApiError, +}; -use super::{check_if_message_exists, BodyChatId}; +use super::{BodyChatId, BotApiError}; +use crate::{ + server::{routes::make_telegram_result, EditedMessageText}, + state::State, +}; #[derive(Debug, Deserialize, Clone)] pub struct EditMessageTextBody { @@ -15,31 +22,45 @@ pub struct EditMessageTextBody { pub parse_mode: Option, pub entities: Option>, pub link_preview_options: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, + pub business_connection_id: Option, } -pub async fn edit_message_text(body: web::Json) -> impl Responder { +pub async fn edit_message_text( + body: web::Json, + state: web::Data>, +) -> impl Responder { match ( body.chat_id.clone(), body.message_id, body.inline_message_id.clone(), ) { (Some(_), Some(message_id), None) => { - check_if_message_exists!(message_id); + let mut lock = state.lock().unwrap(); + let Some(old_message) = lock.messages.get_message(message_id) else { + return BotApiError::new(ApiError::MessageToEditNotFound).error_response(); + }; + + let old_reply_markup = old_message + .reply_markup() + .map(|kb| ReplyMarkup::InlineKeyboard(kb.clone())); + if old_message.text() == Some(&body.text) && old_reply_markup == body.reply_markup { + return BotApiError::new(ApiError::MessageNotModified).error_response(); + } - MESSAGES.edit_message(message_id, "text", body.text.clone()); - MESSAGES.edit_message( + lock.messages + .edit_message_field(message_id, "text", body.text.clone()); + lock.messages.edit_message_field( message_id, "entities", body.entities.clone().unwrap_or(vec![]), ); - let message = MESSAGES + let message = lock + .messages .edit_message_reply_markup(message_id, body.reply_markup.clone()) .unwrap(); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.edited_messages_text.push(EditedMessageText { + lock.responses.edited_messages_text.push(EditedMessageText { message: message.clone(), bot_request: body.into_inner(), }); diff --git a/teloxide_tests/src/server/routes/forward_message.rs b/teloxide_tests/src/server/routes/forward_message.rs index 2ee279d..fb71573 100644 --- a/teloxide_tests/src/server/routes/forward_message.rs +++ b/teloxide_tests/src/server/routes/forward_message.rs @@ -1,11 +1,14 @@ -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; +use std::sync::Mutex; + +use actix_web::{error::ErrorBadRequest, web, Responder}; use serde::Deserialize; -use teloxide::types::{ChatKind, Me, MessageId, MessageKind, MessageOrigin, PublicChatKind}; -use crate::server::ForwardedMessage; -use crate::server::{routes::check_if_message_exists, MESSAGES, RESPONSES}; +use teloxide::types::{Me, MessageId, MessageKind, MessageOrigin}; use super::{make_telegram_result, BodyChatId}; +use crate::{ + server::{routes::check_if_message_exists, ForwardedMessage}, + state::State, +}; #[derive(Debug, Deserialize, Clone)] pub struct ForwardMessageBody { @@ -20,9 +23,12 @@ pub struct ForwardMessageBody { pub async fn forward_message( body: web::Json, me: web::Data, + state: web::Data>, ) -> impl Responder { - check_if_message_exists!(body.message_id); - let mut message = MESSAGES.get_message(body.message_id).unwrap(); + let mut lock = state.lock().unwrap(); + + check_if_message_exists!(lock, body.message_id); + let mut message = lock.messages.get_message(body.message_id).unwrap(); if message.has_protected_content() { return ErrorBadRequest("Message has protected content").into(); @@ -30,47 +36,42 @@ pub async fn forward_message( let message_clone = message.clone(); if let MessageKind::Common(ref mut common) = message.kind { - common.forward_origin = Some(match message.chat.kind { - ChatKind::Private(_) => match message.from { - Some(ref user) => MessageOrigin::User { - date: message_clone.date, - sender_user: user.clone(), - }, - None => MessageOrigin::HiddenUser { - date: message_clone.date, - sender_user_name: message_clone - .chat - .username() - .unwrap_or("no_username") - .to_string(), - }, - }, - ChatKind::Public(public_chat) => match public_chat.kind { - PublicChatKind::Group(_) => MessageOrigin::Chat { - date: message_clone.date, - sender_chat: message_clone.chat, - author_signature: None, - }, - _ => MessageOrigin::Channel { - date: message_clone.date, - chat: message_clone.chat, - message_id: message_clone.id, - author_signature: None, - }, - }, + common.forward_origin = Some(if message.chat.is_channel() { + MessageOrigin::Channel { + date: message_clone.date, + chat: message_clone.chat, + message_id: message_clone.id, + author_signature: None, + } + } else if let Some(sender_chat) = &message.sender_chat { + MessageOrigin::Chat { + date: message_clone.date, + sender_chat: sender_chat.clone(), + author_signature: None, + } + } else if let Some(user) = &message.from { + MessageOrigin::User { + date: message_clone.date, + sender_user: user.clone(), + } + } else { + // This is probably unreachable. + MessageOrigin::HiddenUser { + date: message_clone.date, + sender_user_name: "Unknown user".to_string(), + } }); common.has_protected_content = body.protect_content.unwrap_or(false); } - let last_id = MESSAGES.max_message_id(); + let last_id = lock.messages.max_message_id(); message.id = MessageId(last_id + 1); message.chat = body.chat_id.chat(); message.from = Some(me.user.clone()); - let message = MESSAGES.add_message(message); + let message = lock.messages.add_message(message); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.forwarded_messages.push(ForwardedMessage { + lock.responses.sent_messages.push(message.clone()); + lock.responses.forwarded_messages.push(ForwardedMessage { message: message.clone(), bot_request: body.into_inner(), }); diff --git a/teloxide_tests/src/server/routes/get_file.rs b/teloxide_tests/src/server/routes/get_file.rs index a8af917..ce12320 100644 --- a/teloxide_tests/src/server/routes/get_file.rs +++ b/teloxide_tests/src/server/routes/get_file.rs @@ -1,18 +1,22 @@ +use std::sync::Mutex; + use actix_web::{error::ErrorBadRequest, web, Responder}; use serde::Deserialize; -use crate::server::FILES; - use super::make_telegram_result; +use crate::state::State; #[derive(Deserialize)] pub struct GetFileQuery { file_id: String, } -pub async fn get_file(query: web::Json) -> impl Responder { - let lock = FILES.lock().unwrap(); - let Some(file) = lock.iter().find(|f| f.id == query.file_id) else { +pub async fn get_file( + query: web::Json, + state: web::Data>, +) -> impl Responder { + let lock = state.lock().unwrap(); + let Some(file) = lock.files.iter().find(|f| f.id == query.file_id) else { return ErrorBadRequest("File not found").into(); }; make_telegram_result(file) diff --git a/teloxide_tests/src/server/routes/get_me.rs b/teloxide_tests/src/server/routes/get_me.rs new file mode 100644 index 0000000..1999dde --- /dev/null +++ b/teloxide_tests/src/server/routes/get_me.rs @@ -0,0 +1,8 @@ +use actix_web::{web, Responder}; +use teloxide::types::Me; + +use super::make_telegram_result; + +pub async fn get_me(me: web::Data) -> impl Responder { + make_telegram_result(me) +} diff --git a/teloxide_tests/src/server/routes/get_updates.rs b/teloxide_tests/src/server/routes/get_updates.rs new file mode 100644 index 0000000..8ea31e9 --- /dev/null +++ b/teloxide_tests/src/server/routes/get_updates.rs @@ -0,0 +1,8 @@ +use actix_web::Responder; +use serde_json::json; + +use super::make_telegram_result; + +pub async fn get_updates() -> impl Responder { + make_telegram_result(json!([])) +} diff --git a/teloxide_tests/src/server/routes/get_webhook_info.rs b/teloxide_tests/src/server/routes/get_webhook_info.rs new file mode 100644 index 0000000..98006c6 --- /dev/null +++ b/teloxide_tests/src/server/routes/get_webhook_info.rs @@ -0,0 +1,10 @@ +use actix_web::Responder; +use serde_json::json; + +use super::make_telegram_result; + +pub async fn get_webhook_info() -> impl Responder { + make_telegram_result( + json!({"url": "", "has_custom_certificate":false,"pending_update_count":0}), + ) +} diff --git a/teloxide_tests/src/server/routes/mod.rs b/teloxide_tests/src/server/routes/mod.rs index b9ff2fa..743a660 100644 --- a/teloxide_tests/src/server/routes/mod.rs +++ b/teloxide_tests/src/server/routes/mod.rs @@ -1,28 +1,31 @@ -use std::collections::HashMap; -use std::str::from_utf8; +use std::{collections::HashMap, str::from_utf8}; -use crate::dataset::{MockPrivateChat, MockSupergroupChat}; -use actix_web::HttpResponse; -use futures_util::stream::StreamExt as _; -use futures_util::TryStreamExt; -use rand::distributions::{Alphanumeric, DistString}; +use actix_web::{error::ResponseError, http::header::ContentType, HttpResponse}; +use futures_util::{stream::StreamExt as _, TryStreamExt}; +use rand::distr::{Alphanumeric, SampleString}; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; -use teloxide::types::{ - Chat, ForceReply, KeyboardMarkup, KeyboardRemove, MessageEntity, ParseMode, ReplyMarkup, - Seconds, True, +use serde_json::json; +use teloxide::{ + types::{Chat, MessageEntity, ParseMode, Seconds}, + ApiError, }; +use crate::dataset::{MockPrivateChat, MockSupergroupChat}; + pub mod answer_callback_query; pub mod ban_chat_member; pub mod copy_message; pub mod delete_message; +pub mod delete_messages; pub mod download_file; pub mod edit_message_caption; pub mod edit_message_reply_markup; pub mod edit_message_text; pub mod forward_message; pub mod get_file; +pub mod get_me; +pub mod get_updates; +pub mod get_webhook_info; pub mod pin_chat_message; pub mod restrict_chat_member; pub mod send_animation; @@ -31,6 +34,7 @@ pub mod send_chat_action; pub mod send_contact; pub mod send_dice; pub mod send_document; +pub mod send_invoice; pub mod send_location; pub mod send_media_group; pub mod send_message; @@ -41,10 +45,11 @@ pub mod send_venue; pub mod send_video; pub mod send_video_note; pub mod send_voice; +pub mod set_message_reaction; +pub mod set_my_commands; pub mod unban_chat_member; pub mod unpin_all_chat_messages; pub mod unpin_chat_message; -pub mod set_message_reaction; /// Telegram accepts both `i64` and `String` for chat_id, /// so it is a wrapper for both @@ -165,9 +170,49 @@ pub trait SerializeRawFields { Self: Sized; } +#[derive(Debug, Serialize)] +struct TelegramResponse { + ok: bool, + description: String, +} + +#[derive(Debug, PartialEq, Hash, Eq, Clone)] +struct BotApiError { + error: ApiError, +} + +impl BotApiError { + pub fn new(error: ApiError) -> Self { + Self { error } + } +} + +impl std::fmt::Display for BotApiError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.error.fmt(f) + } +} + +impl ResponseError for BotApiError { + fn status_code(&self) -> actix_web::http::StatusCode { + actix_web::http::StatusCode::BAD_REQUEST + } + + fn error_response(&self) -> HttpResponse { + let response = TelegramResponse { + ok: false, + description: self.error.to_string(), + }; + HttpResponse::build(self.status_code()) + .insert_header(ContentType::json()) + .body(serde_json::to_string(&response).unwrap()) + } +} + +// TODO: replace usages with appropriate error values from teloxide::ApiError. macro_rules! check_if_message_exists { - ($msg_id:expr) => { - if MESSAGES.get_message($msg_id).is_none() { + ($lock:expr, $msg_id:expr) => { + if $lock.messages.get_message($msg_id).is_none() { return ErrorBadRequest("Message not found").into(); } }; @@ -202,10 +247,7 @@ pub async fn get_raw_multipart_fields( .enumerate() .map(|(i, s)| { if i == 0 { - format!( - "{s}{}", - Alphanumeric.sample_string(&mut rand::thread_rng(), 5) - ) + format!("{s}{}", Alphanumeric.sample_string(&mut rand::rng(), 5)) } else { s.to_string() } @@ -236,7 +278,9 @@ pub async fn get_raw_multipart_fields( Attachment { raw_name: data.0.to_string(), file_name: filename.to_string(), - file_data: from_utf8(&data.1).unwrap().to_string(), + file_data: from_utf8(&data.1) + .unwrap_or("error_getting_data") + .to_string(), }, ); } @@ -256,111 +300,3 @@ where .to_string(), ) } - -pub fn deserialize_reply_markup(value: Value) -> Option { - let selective = value - .get("selective") - .map(|x| serde_json::from_value(x.clone()).ok()) - .flatten() - == Some(true); - let input_field_placeholder: Option = value - .get("input_field_placeholder") - .map(|x| serde_json::from_value(x.clone()).ok()) - .flatten(); - if value.get("keyboard").is_some() { - let is_persistent: bool = value - .get("is_persistent") - .map(|x| serde_json::from_value(x.clone()).ok()) - .flatten() - == Some(true); - let one_time_keyboard = value - .get("one_time_keyboard") - .map(|x| serde_json::from_value(x.clone()).ok()) - .flatten() - == Some(true); - let resize_keyboard = value - .get("resize_keyboard") - .map(|x| serde_json::from_value(x.clone()).ok()) - .flatten() - == Some(true); - - return Some(ReplyMarkup::Keyboard(KeyboardMarkup { - keyboard: serde_json::from_value(value["keyboard"].clone()).unwrap(), - is_persistent, - selective, - input_field_placeholder: input_field_placeholder.unwrap_or("".to_string()), - one_time_keyboard, - resize_keyboard, - })); - } else if value.get("inline_keyboard").is_some() { - return serde_json::from_value(value).ok(); - } else if value.get("remove_keyboard").is_some() { - return Some(ReplyMarkup::KeyboardRemove(KeyboardRemove { - remove_keyboard: True, - selective, - })); - } else if value.get("force_reply").is_some() { - return Some(ReplyMarkup::ForceReply(ForceReply { - force_reply: True, - input_field_placeholder, - selective, - })); - } - - return None; -} - -pub(crate) mod reply_markup_deserialize { - use super::deserialize_reply_markup; - use serde::{Deserialize, Deserializer}; - use serde_json::Value; - use teloxide::types::ReplyMarkup; - - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let value: Option = Option::deserialize(deserializer)?; - match value { - Some(value) => { - if !value.is_null() { - Ok(deserialize_reply_markup(value)) - } else { - Ok(None) - } - } - None => { - Ok(None) - } - } - } - - #[test] - fn test() { - use teloxide::types::KeyboardRemove; - #[derive(serde::Deserialize, Debug, PartialEq)] - struct Struct { - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] - reply_markup: Option, - } - - { - let s: Struct = - serde_json::from_str("{\"reply_markup\": {\"remove_keyboard\":\"True\"}}").unwrap(); - assert_eq!( - s, - Struct { - reply_markup: Some(ReplyMarkup::KeyboardRemove(KeyboardRemove { - remove_keyboard: teloxide::types::True, - selective: false - })) - } - ); - } - - { - let s: Struct = serde_json::from_str("{}").unwrap(); - assert_eq!(s, Struct { reply_markup: None }) - } - } -} diff --git a/teloxide_tests/src/server/routes/pin_chat_message.rs b/teloxide_tests/src/server/routes/pin_chat_message.rs index e5bdd3c..a879ef5 100644 --- a/teloxide_tests/src/server/routes/pin_chat_message.rs +++ b/teloxide_tests/src/server/routes/pin_chat_message.rs @@ -1,23 +1,26 @@ -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; +use std::sync::Mutex; -use crate::server::routes::make_telegram_result; -use crate::server::{MESSAGES, RESPONSES}; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; +use teloxide::types::BusinessConnectionId; use super::{check_if_message_exists, BodyChatId}; +use crate::{server::routes::make_telegram_result, state::State}; #[derive(Debug, Deserialize, Clone)] pub struct PinChatMessageBody { pub chat_id: BodyChatId, pub message_id: i32, pub disable_notification: Option, + pub business_connection_id: Option, } -pub async fn pin_chat_message(body: web::Json) -> impl Responder { - check_if_message_exists!(body.message_id); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.pinned_chat_messages.push(body.into_inner()); - +pub async fn pin_chat_message( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); + check_if_message_exists!(lock, body.message_id); + lock.responses.pinned_chat_messages.push(body.into_inner()); make_telegram_result(true) } diff --git a/teloxide_tests/src/server/routes/restrict_chat_member.rs b/teloxide_tests/src/server/routes/restrict_chat_member.rs index 0ebedee..386385e 100644 --- a/teloxide_tests/src/server/routes/restrict_chat_member.rs +++ b/teloxide_tests/src/server/routes/restrict_chat_member.rs @@ -1,11 +1,11 @@ +use std::sync::Mutex; + use actix_web::{web, Responder}; use serde::Deserialize; use teloxide::types::ChatPermissions; -use crate::server::routes::make_telegram_result; -use crate::server::RESPONSES; - use super::BodyChatId; +use crate::{server::routes::make_telegram_result, state::State}; #[derive(Debug, Deserialize, Clone)] pub struct RestrictChatMemberBody { @@ -16,10 +16,13 @@ pub struct RestrictChatMemberBody { pub until_date: Option, } -pub async fn restrict_chat_member(body: web::Json) -> impl Responder { +pub async fn restrict_chat_member( + state: web::Data>, + body: web::Json, +) -> impl Responder { // Idk what to verify here - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock + let mut lock = state.lock().unwrap(); + lock.responses .restricted_chat_members .push(body.into_inner()); diff --git a/teloxide_tests/src/server/routes/send_animation.rs b/teloxide_tests/src/server/routes/send_animation.rs index 700275a..516ca41 100644 --- a/teloxide_tests/src/server/routes/send_animation.rs +++ b/teloxide_tests/src/server/routes/send_animation.rs @@ -1,25 +1,32 @@ -use crate::server::routes::Attachment; -use crate::server::routes::{FileType, SerializeRawFields}; -use crate::server::SentMessageAnimation; -use crate::MockMessageAnimation; -use std::collections::HashMap; -use std::str::FromStr; +use std::{collections::HashMap, str::FromStr, sync::Mutex}; -use crate::proc_macros::SerializeRawFields; use actix_multipart::Multipart; -use actix_web::Responder; -use actix_web::{error::ErrorBadRequest, web}; +use actix_web::{error::ErrorBadRequest, web, Responder}; use mime::Mime; -use rand::distributions::{Alphanumeric, DistString}; +use rand::distr::{Alphanumeric, SampleString}; use serde::Deserialize; -use teloxide::types::{Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, Seconds}; - -use crate::server::{routes::check_if_message_exists, FILES, MESSAGES, RESPONSES}; +use teloxide::types::{ + BusinessConnectionId, Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, Seconds, +}; use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; +use crate::{ + proc_macros::SerializeRawFields, + server::{ + routes::{check_if_message_exists, Attachment, FileType, SerializeRawFields}, + SentMessageAnimation, + }, + state::State, + MockMessageAnimation, +}; -pub async fn send_animation(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_animation( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMessageAnimationBody::serialize_raw_fields(&fields, &attachments, FileType::Animation) .unwrap(); @@ -32,18 +39,24 @@ pub async fn send_animation(mut payload: Multipart, me: web::Data) -> impl R message.caption = body.caption.clone(); message.caption_entities = body.caption_entities.clone().unwrap_or_default(); message.has_media_spoiler = body.has_spoiler.unwrap_or_default(); + message.effect_id = body.message_effect_id.clone(); + message.show_caption_above_media = body.show_caption_above_media.unwrap_or(false); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let file_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); - let file_unique_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); + let file_id = Alphanumeric.sample_string(&mut rand::rng(), 16); + let file_unique_id = Alphanumeric.sample_string(&mut rand::rng(), 8); message.file_name = Some(body.file_name.clone()); message.file_id = file_id.clone(); @@ -58,16 +71,15 @@ pub async fn send_animation(mut payload: Multipart, me: web::Data) -> impl R .unwrap_or(Mime::from_str("image/gif").unwrap()), ); - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.animation().unwrap().file.clone(), path: body.file_name.to_owned(), }); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock + lock.responses.sent_messages.push(message.clone()); + lock.responses .sent_messages_animation .push(SentMessageAnimation { message: message.clone(), @@ -94,7 +106,7 @@ pub struct SendMessageAnimationBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } diff --git a/teloxide_tests/src/server/routes/send_audio.rs b/teloxide_tests/src/server/routes/send_audio.rs index 7907ee1..a6a09ce 100644 --- a/teloxide_tests/src/server/routes/send_audio.rs +++ b/teloxide_tests/src/server/routes/send_audio.rs @@ -1,28 +1,32 @@ -use crate::server::routes::Attachment; -use crate::{ - server::{ - routes::{FileType, SerializeRawFields}, - SentMessageAudio, - }, - MockMessageAudio, -}; -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, str::FromStr, sync::Mutex}; -use crate::proc_macros::SerializeRawFields; use actix_multipart::Multipart; -use actix_web::Responder; -use actix_web::{error::ErrorBadRequest, web}; +use actix_web::{error::ErrorBadRequest, web, Responder}; use mime::Mime; -use rand::distributions::{Alphanumeric, DistString}; +use rand::distr::{Alphanumeric, SampleString}; use serde::Deserialize; -use teloxide::types::{Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, Seconds}; - -use crate::server::{routes::check_if_message_exists, FILES, MESSAGES, RESPONSES}; +use teloxide::types::{ + BusinessConnectionId, Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, Seconds, +}; use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; +use crate::{ + proc_macros::SerializeRawFields, + server::{ + routes::{check_if_message_exists, Attachment, FileType, SerializeRawFields}, + SentMessageAudio, + }, + state::State, + MockMessageAudio, +}; -pub async fn send_audio(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_audio( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMessageAudioBody::serialize_raw_fields(&fields, &attachments, FileType::Audio).unwrap(); let chat = body.chat_id.chat(); @@ -32,18 +36,23 @@ pub async fn send_audio(mut payload: Multipart, me: web::Data) -> impl Respo message.from = Some(me.user.clone()); message.caption = body.caption.clone(); message.caption_entities = body.caption_entities.clone().unwrap_or_default(); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let file_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); - let file_unique_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); + let file_id = Alphanumeric.sample_string(&mut rand::rng(), 16); + let file_unique_id = Alphanumeric.sample_string(&mut rand::rng(), 8); message.file_id = file_id.clone(); message.file_unique_id = file_unique_id.clone(); @@ -54,16 +63,15 @@ pub async fn send_audio(mut payload: Multipart, me: web::Data) -> impl Respo message.mime_type = Some(Mime::from_str("audio/mp3").unwrap()); message.file_name = Some(body.file_name.clone()); - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.audio().unwrap().file.clone(), path: body.file_name.to_owned(), }); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.sent_messages_audio.push(SentMessageAudio { + lock.responses.sent_messages.push(message.clone()); + lock.responses.sent_messages_audio.push(SentMessageAudio { message: message.clone(), bot_request: body, }); @@ -87,6 +95,6 @@ pub struct SendMessageAudioBody { pub protect_content: Option, pub message_effect_id: Option, pub reply_parameters: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, + pub business_connection_id: Option, } diff --git a/teloxide_tests/src/server/routes/send_chat_action.rs b/teloxide_tests/src/server/routes/send_chat_action.rs index 66ac896..5f4c150 100644 --- a/teloxide_tests/src/server/routes/send_chat_action.rs +++ b/teloxide_tests/src/server/routes/send_chat_action.rs @@ -1,21 +1,26 @@ +use std::sync::Mutex; + use actix_web::{web, Responder}; use serde::Deserialize; - -use crate::server::routes::make_telegram_result; -use crate::server::RESPONSES; +use teloxide::types::BusinessConnectionId; use super::BodyChatId; +use crate::{server::routes::make_telegram_result, state::State}; #[derive(Debug, Deserialize, Clone)] pub struct SendChatActionBody { pub chat_id: BodyChatId, pub message_thread_id: Option, pub action: String, + pub business_connection_id: Option, } -pub async fn send_chat_action(body: web::Json) -> impl Responder { - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_chat_actions.push(body.into_inner()); +pub async fn send_chat_action( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); + lock.responses.sent_chat_actions.push(body.into_inner()); make_telegram_result(true) } diff --git a/teloxide_tests/src/server/routes/send_contact.rs b/teloxide_tests/src/server/routes/send_contact.rs index 846f3b2..6bc0440 100644 --- a/teloxide_tests/src/server/routes/send_contact.rs +++ b/teloxide_tests/src/server/routes/send_contact.rs @@ -1,13 +1,15 @@ -use crate::server::{SentMessageContact, MESSAGES, RESPONSES}; -use crate::MockMessageContact; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; -use teloxide::types::{Me, ReplyMarkup, ReplyParameters}; +use std::sync::Mutex; -use crate::server::routes::check_if_message_exists; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; +use teloxide::types::{BusinessConnectionId, Me, ReplyMarkup, ReplyParameters}; use super::{make_telegram_result, BodyChatId}; +use crate::{ + server::{routes::check_if_message_exists, SentMessageContact}, + state::State, + MockMessageContact, +}; #[derive(Debug, Deserialize, Clone)] pub struct SendMessageContactBody { @@ -20,15 +22,17 @@ pub struct SendMessageContactBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } pub async fn send_contact( body: web::Json, me: web::Data, + state: web::Data>, ) -> impl Responder { + let mut lock = state.lock().unwrap(); let chat = body.chat_id.chat(); let mut message = // Creates the message, which will be mutated to fit the needed shape MockMessageContact::new().chat(chat); @@ -38,22 +42,26 @@ pub async fn send_contact( message.last_name = body.last_name.clone(); message.vcard = body.vcard.clone(); message.has_protected_content = body.protect_content.unwrap_or(false); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock + lock.responses.sent_messages.push(message.clone()); + lock.responses .sent_messages_contact .push(SentMessageContact { message: message.clone(), diff --git a/teloxide_tests/src/server/routes/send_dice.rs b/teloxide_tests/src/server/routes/send_dice.rs index dbee5a8..8e0cf90 100644 --- a/teloxide_tests/src/server/routes/send_dice.rs +++ b/teloxide_tests/src/server/routes/send_dice.rs @@ -1,12 +1,15 @@ -use crate::server::routes::check_if_message_exists; -use crate::server::{SentMessageDice, MESSAGES, RESPONSES}; -use crate::MockMessageDice; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; +use std::sync::Mutex; + +use actix_web::{error::ErrorBadRequest, web, Responder}; use serde::Deserialize; -use teloxide::types::{DiceEmoji, ReplyMarkup, ReplyParameters}; +use teloxide::types::{BusinessConnectionId, DiceEmoji, ReplyMarkup, ReplyParameters}; use super::{make_telegram_result, BodyChatId}; +use crate::{ + server::{routes::check_if_message_exists, SentMessageDice}, + state::State, + MockMessageDice, +}; #[derive(Debug, Deserialize, Clone)] pub struct SendMessageDiceBody { @@ -16,12 +19,16 @@ pub struct SendMessageDiceBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } -pub async fn send_dice(body: web::Json) -> impl Responder { +pub async fn send_dice( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); let chat = body.chat_id.chat(); let mut message = // Creates the message, which will be mutated to fit the needed shape MockMessageDice::new().chat(chat); @@ -29,15 +36,14 @@ pub async fn send_dice(body: web::Json) -> impl Responder { // Random from 1 to 5 because it fits all the emoji message.value = (1 + rand::random::() % 5) as u8; if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); + check_if_message_exists!(lock, reply_parameters.message_id.0); } - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.sent_messages_dice.push(SentMessageDice { + lock.responses.sent_messages.push(message.clone()); + lock.responses.sent_messages_dice.push(SentMessageDice { message: message.clone(), bot_request: body.into_inner(), }); diff --git a/teloxide_tests/src/server/routes/send_document.rs b/teloxide_tests/src/server/routes/send_document.rs index 8cc07b0..6d848ee 100644 --- a/teloxide_tests/src/server/routes/send_document.rs +++ b/teloxide_tests/src/server/routes/send_document.rs @@ -1,26 +1,32 @@ -use crate::server::routes::Attachment; -use crate::server::routes::{FileType, SerializeRawFields}; -use std::collections::HashMap; -use std::str::FromStr; +use std::{collections::HashMap, str::FromStr, sync::Mutex}; -use crate::dataset::MockMessageDocument; -use crate::proc_macros::SerializeRawFields; use actix_multipart::Multipart; -use actix_web::Responder; -use actix_web::{error::ErrorBadRequest, web}; +use actix_web::{error::ErrorBadRequest, web, Responder}; use mime::Mime; -use rand::distributions::{Alphanumeric, DistString}; +use rand::distr::{Alphanumeric, SampleString}; use serde::Deserialize; -use teloxide::types::{Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters}; - -use crate::server::{ - routes::check_if_message_exists, SentMessageDocument, FILES, MESSAGES, RESPONSES, +use teloxide::types::{ + BusinessConnectionId, Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, }; use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; +use crate::{ + dataset::MockMessageDocument, + proc_macros::SerializeRawFields, + server::{ + routes::{check_if_message_exists, Attachment, FileType, SerializeRawFields}, + SentMessageDocument, + }, + state::State, +}; -pub async fn send_document(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_document( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMessageDocumentBody::serialize_raw_fields(&fields, &attachments, FileType::Document) .unwrap(); @@ -31,18 +37,23 @@ pub async fn send_document(mut payload: Multipart, me: web::Data) -> impl Re message.from = Some(me.user.clone()); message.caption = body.caption.clone(); message.caption_entities = body.caption_entities.clone().unwrap_or_default(); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let file_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); - let file_unique_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); + let file_id = Alphanumeric.sample_string(&mut rand::rng(), 16); + let file_unique_id = Alphanumeric.sample_string(&mut rand::rng(), 8); message.file_name = Some(body.file_name.clone()); message.file_id = file_id.clone(); @@ -55,16 +66,15 @@ pub async fn send_document(mut payload: Multipart, me: web::Data) -> impl Re ); message.has_protected_content = body.protect_content.unwrap_or(false); - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.document().unwrap().file.clone(), path: body.file_name.to_owned(), }); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock + lock.responses.sent_messages.push(message.clone()); + lock.responses .sent_messages_document .push(SentMessageDocument { message: message.clone(), @@ -87,7 +97,7 @@ pub struct SendMessageDocumentBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } diff --git a/teloxide_tests/src/server/routes/send_invoice.rs b/teloxide_tests/src/server/routes/send_invoice.rs new file mode 100644 index 0000000..8f668e6 --- /dev/null +++ b/teloxide_tests/src/server/routes/send_invoice.rs @@ -0,0 +1,85 @@ +use std::sync::Mutex; + +use actix_web::{web, Responder}; +use serde::Deserialize; +use teloxide::types::{LabeledPrice, Me, ReplyMarkup, ReplyParameters}; + +use super::{make_telegram_result, BodyChatId}; +use crate::{server::SentMessageInvoice, state::State, MockMessageInvoice}; + +#[derive(Debug, Deserialize, Clone)] +pub struct SendMessageInvoiceBody { + pub chat_id: BodyChatId, + pub message_thread_id: Option, + pub title: String, + pub description: String, + pub payload: String, + pub provider_token: Option, + pub currency: String, + pub prices: Vec, + pub max_tip_amount: Option, + pub suggested_tip_amounts: Option>, + pub start_parameter: Option, + pub provider_data: Option, + pub photo_url: Option, + pub photo_size: Option, + pub photo_width: Option, + pub photo_height: Option, + pub need_name: Option, + pub need_phone_number: Option, + pub need_email: Option, + pub need_shipping_address: Option, + pub send_phone_number_to_provider: Option, + pub send_email_to_provider: Option, + pub is_flexible: Option, + pub disable_notification: Option, + pub protect_content: Option, + pub message_effect_id: Option, + pub reply_parameters: Option, + pub reply_markup: Option, +} + +pub async fn send_invoice( + body: web::Json, + me: web::Data, + state: web::Data>, +) -> impl Responder { + let mut lock = state.lock().unwrap(); + + let chat = body.chat_id.chat(); + let mut message = MockMessageInvoice::new() + .chat(chat) + .title(body.title.clone()) + .description(body.description.clone()) + .start_parameter(body.start_parameter.clone().unwrap_or("".to_owned())) + .total_amount(body.prices.first().unwrap().amount); + message.from = Some(me.user.clone()); + + // Commented until teloxides new release + // message.has_protected_content = body.protect_content.unwrap_or(false); + + // if let Some(reply_parameters) = &body.reply_parameters { + // check_if_message_exists!(lock, reply_parameters.message_id.0); + // let reply_to_message = lock + // .messages + // .get_message(reply_parameters.message_id.0) + // .unwrap(); + // message.reply_to_message = Some(Box::new(reply_to_message.clone())); + // } + // if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { + // message.reply_markup = Some(markup); + // } + + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); + + lock.responses.sent_messages.push(message.clone()); + lock.responses + .sent_messages_invoice + .push(SentMessageInvoice { + message: message.clone(), + bot_request: body.into_inner(), + }); + + make_telegram_result(message) +} diff --git a/teloxide_tests/src/server/routes/send_location.rs b/teloxide_tests/src/server/routes/send_location.rs index d16be6f..381d802 100644 --- a/teloxide_tests/src/server/routes/send_location.rs +++ b/teloxide_tests/src/server/routes/send_location.rs @@ -1,13 +1,15 @@ -use crate::server::{SentMessageLocation, MESSAGES, RESPONSES}; -use crate::MockMessageLocation; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; -use teloxide::types::{Me, ReplyMarkup, ReplyParameters, Seconds}; +use std::sync::Mutex; -use crate::server::routes::check_if_message_exists; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; +use teloxide::types::{BusinessConnectionId, LivePeriod, Me, ReplyMarkup, ReplyParameters}; use super::{make_telegram_result, BodyChatId}; +use crate::{ + server::{routes::check_if_message_exists, SentMessageLocation}, + state::State, + MockMessageLocation, +}; #[derive(Debug, Deserialize, Clone)] pub struct SendMessageLocationBody { @@ -15,22 +17,25 @@ pub struct SendMessageLocationBody { pub latitude: f64, pub longitude: f64, pub horizontal_accuracy: Option, - pub live_period: Option, + pub live_period: Option, pub heading: Option, pub proximity_alert_radius: Option, pub message_thread_id: Option, pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } pub async fn send_location( body: web::Json, me: web::Data, + state: web::Data>, ) -> impl Responder { + let mut lock = state.lock().unwrap(); + let chat = body.chat_id.chat(); let mut message = // Creates the message, which will be mutated to fit the needed shape MockMessageLocation::new().chat(chat).latitude(body.latitude).longitude(body.longitude); @@ -40,22 +45,26 @@ pub async fn send_location( message.heading = body.heading; message.proximity_alert_radius = body.proximity_alert_radius; message.has_protected_content = body.protect_content.unwrap_or(false); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock + lock.responses.sent_messages.push(message.clone()); + lock.responses .sent_messages_location .push(SentMessageLocation { message: message.clone(), diff --git a/teloxide_tests/src/server/routes/send_media_group.rs b/teloxide_tests/src/server/routes/send_media_group.rs index 2969b57..23b2d26 100644 --- a/teloxide_tests/src/server/routes/send_media_group.rs +++ b/teloxide_tests/src/server/routes/send_media_group.rs @@ -1,28 +1,34 @@ -use crate::server::{SentMediaGroup, FILES, MESSAGES, RESPONSES}; -use crate::{ - MockMessageAudio, MockMessageDocument, MockMessagePhoto, MockMessageVideo, MockPhotoSize, - MockVideo, -}; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Mutex}; use actix_multipart::Multipart; -use actix_web::Responder; -use actix_web::{error::ErrorBadRequest, web}; -use rand::distributions::{Alphanumeric, DistString}; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use rand::distr::{Alphanumeric, SampleString}; use serde::Deserialize; use serde_json::Value; -use teloxide::types::{Me, Message, MessageEntity, MessageId, ParseMode, ReplyParameters, Seconds}; - -use crate::server::routes::check_if_message_exists; +use teloxide::types::{ + BusinessConnectionId, Me, Message, MessageEntity, MessageId, ParseMode, ReplyParameters, + Seconds, +}; use super::{ get_raw_multipart_fields, make_telegram_result, Attachment, BodyChatId, MediaGroupInputMedia, MediaGroupInputMediaAudio, MediaGroupInputMediaDocument, MediaGroupInputMediaPhoto, MediaGroupInputMediaVideo, }; +use crate::{ + server::{routes::check_if_message_exists, SentMediaGroup}, + state::State, + MockMessageAudio, MockMessageDocument, MockMessagePhoto, MockMessageVideo, MockPhotoSize, + MockVideo, +}; -pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_media_group( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMediaGroupBody::serialize_raw_fields(&fields, &attachments).unwrap(); if body.media.len() > 10 { return ErrorBadRequest("Too many media items").into(); @@ -31,20 +37,26 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl } let chat = body.chat_id.chat(); let protect_content = body.protect_content; + let message_effect_id = body.message_effect_id.clone(); + let business_connection_id = body.business_connection_id.clone(); let mut reply_to_message = None; if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); + check_if_message_exists!(lock, reply_parameters.message_id.0); // All of messages in the media group are replying to the same message - reply_to_message = Some(Box::new(MESSAGES.get_message(reply_parameters.message_id.0).unwrap())); + reply_to_message = Some(Box::new( + lock.messages + .get_message(reply_parameters.message_id.0) + .unwrap(), + )); } - let media_group_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); + let media_group_id = Alphanumeric.sample_string(&mut rand::rng(), 16); let mut messages: Vec = vec![]; for media in &body.media { - let file_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); - let file_unique_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); - let last_id = MESSAGES.max_message_id(); + let file_id = Alphanumeric.sample_string(&mut rand::rng(), 16); + let file_unique_id = Alphanumeric.sample_string(&mut rand::rng(), 8); + let last_id = lock.messages.max_message_id(); let message: Message; match media { MediaGroupInputMedia::InputMediaAudio(audio) => { @@ -60,6 +72,8 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl mock_message.performer = audio.performer.clone(); mock_message.title = audio.title.clone(); mock_message.duration = audio.duration.unwrap_or(Seconds::from_seconds(1)); + mock_message.effect_id = message_effect_id.clone(); + mock_message.business_connection_id = business_connection_id.clone(); mock_message.file_name = Some(audio.file_name.clone()); mock_message.file_id = file_id.clone(); @@ -70,7 +84,7 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl mock_message.id = MessageId(last_id + 1); message = mock_message.build(); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.audio().unwrap().file.clone(), path: audio.file_name.clone(), }); @@ -86,6 +100,8 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl mock_message.caption_entities = document.caption_entities.clone().unwrap_or_default(); mock_message.media_group_id = Some(media_group_id.clone()); + mock_message.effect_id = message_effect_id.clone(); + mock_message.business_connection_id = business_connection_id.clone(); mock_message.file_name = Some(document.file_name.clone()); mock_message.file_id = file_id.clone(); @@ -96,7 +112,7 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl mock_message.id = MessageId(last_id + 1); message = mock_message.build(); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.document().unwrap().file.clone(), path: document.file_name.clone(), }); @@ -111,6 +127,8 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl mock_message.caption = photo.caption.clone(); mock_message.caption_entities = photo.caption_entities.clone().unwrap_or_default(); mock_message.media_group_id = Some(media_group_id.clone()); + mock_message.effect_id = message_effect_id.clone(); + mock_message.business_connection_id = business_connection_id.clone(); let mut mock_photo = MockPhotoSize::new(); @@ -123,7 +141,7 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl mock_message.id = MessageId(last_id + 1); message = mock_message.build(); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.photo().unwrap().first().unwrap().clone().file, path: photo.file_name.clone(), }); @@ -138,6 +156,8 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl mock_message.caption = video.caption.clone(); mock_message.caption_entities = video.caption_entities.clone().unwrap_or_default(); mock_message.media_group_id = Some(media_group_id.clone()); + mock_message.effect_id = message_effect_id.clone(); + mock_message.business_connection_id = business_connection_id.clone(); let mut mock_video = MockVideo::new(); @@ -155,7 +175,7 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl mock_message.id = MessageId(last_id + 1); message = mock_message.build(); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.video().unwrap().file.clone(), path: video.file_name.clone(), }); @@ -163,12 +183,11 @@ pub async fn send_media_group(mut payload: Multipart, me: web::Data) -> impl } messages.push(message.clone()); - MESSAGES.add_message(message); + lock.messages.add_message(message); } - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.extend(messages.clone()); - responses_lock.sent_media_group.push(SentMediaGroup { + lock.responses.sent_messages.extend(messages.clone()); + lock.responses.sent_media_group.push(SentMediaGroup { messages: messages.clone(), bot_request: body, }); @@ -184,6 +203,7 @@ pub struct SendMediaGroupBody { pub protect_content: Option, pub message_effect_id: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } impl SendMediaGroupBody { @@ -321,6 +341,9 @@ impl SendMediaGroupBody { reply_parameters: fields .get("reply_parameters") .map(|s| serde_json::from_str(s).unwrap()), + business_connection_id: fields + .get("business_connection_id") + .map(|s| serde_json::from_str(s).unwrap()), }) } } diff --git a/teloxide_tests/src/server/routes/send_message.rs b/teloxide_tests/src/server/routes/send_message.rs index 53b1e86..9cada8a 100644 --- a/teloxide_tests/src/server/routes/send_message.rs +++ b/teloxide_tests/src/server/routes/send_message.rs @@ -1,14 +1,18 @@ -use crate::dataset::message_common::MockMessageText; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; +use std::sync::Mutex; + +use actix_web::{error::ErrorBadRequest, web, Responder}; use serde::Deserialize; use teloxide::types::{ - LinkPreviewOptions, Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, + BusinessConnectionId, LinkPreviewOptions, Me, MessageEntity, ParseMode, ReplyMarkup, + ReplyParameters, }; -use crate::server::{routes::check_if_message_exists, SentMessageText, MESSAGES, RESPONSES}; - use super::{make_telegram_result, BodyChatId}; +use crate::{ + dataset::message_common::MockMessageText, + server::{routes::check_if_message_exists, SentMessageText}, + state::State, +}; #[derive(Debug, Deserialize, Clone)] pub struct SendMessageTextBody { @@ -21,37 +25,43 @@ pub struct SendMessageTextBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } pub async fn send_message( body: web::Json, me: web::Data, + state: web::Data>, ) -> impl Responder { + let mut lock = state.lock().unwrap(); let chat = body.chat_id.chat(); let mut message = // Creates the message, which will be mutated to fit the needed shape MockMessageText::new().text(&body.text).chat(chat); message.from = Some(me.user.clone()); message.has_protected_content = body.protect_content.unwrap_or(false); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); message.entities = body.entities.clone().unwrap_or_default(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.sent_messages_text.push(SentMessageText { + lock.responses.sent_messages.push(message.clone()); + lock.responses.sent_messages_text.push(SentMessageText { message: message.clone(), bot_request: body.into_inner(), }); diff --git a/teloxide_tests/src/server/routes/send_photo.rs b/teloxide_tests/src/server/routes/send_photo.rs index 327d1e3..059b472 100644 --- a/teloxide_tests/src/server/routes/send_photo.rs +++ b/teloxide_tests/src/server/routes/send_photo.rs @@ -1,24 +1,32 @@ -use crate::server::routes::Attachment; -use crate::server::routes::{FileType, SerializeRawFields}; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Mutex}; -use crate::dataset::{MockMessagePhoto, MockPhotoSize}; -use crate::proc_macros::SerializeRawFields; use actix_multipart::Multipart; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use rand::distributions::{Alphanumeric, DistString}; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use rand::distr::{Alphanumeric, SampleString}; use serde::Deserialize; -use teloxide::types::{LinkPreviewOptions, Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters}; - -use crate::server::{ - routes::check_if_message_exists, SentMessagePhoto, FILES, MESSAGES, RESPONSES, +use teloxide::types::{ + BusinessConnectionId, LinkPreviewOptions, Me, MessageEntity, ParseMode, ReplyMarkup, + ReplyParameters, }; use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; +use crate::{ + dataset::{MockMessagePhoto, MockPhotoSize}, + proc_macros::SerializeRawFields, + server::{ + routes::{check_if_message_exists, Attachment, FileType, SerializeRawFields}, + SentMessagePhoto, + }, + state::State, +}; -pub async fn send_photo(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_photo( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMessagePhotoBody::serialize_raw_fields(&fields, &attachments, FileType::Photo).unwrap(); let chat = body.chat_id.chat(); @@ -29,18 +37,24 @@ pub async fn send_photo(mut payload: Multipart, me: web::Data) -> impl Respo message.has_protected_content = body.protect_content.unwrap_or(false); message.caption = body.caption.clone(); message.caption_entities = body.caption_entities.clone().unwrap_or_default(); + message.show_caption_above_media = body.show_caption_above_media.unwrap_or(false); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let file_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); - let file_unique_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); + let file_id = Alphanumeric.sample_string(&mut rand::rng(), 16); + let file_unique_id = Alphanumeric.sample_string(&mut rand::rng(), 8); message.photo = vec![MockPhotoSize::new() .file_id(file_id.clone()) @@ -48,16 +62,15 @@ pub async fn send_photo(mut payload: Multipart, me: web::Data) -> impl Respo .file_size(body.file_data.bytes().len() as u32) .build()]; - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.photo().unwrap()[0].file.clone(), path: body.file_name.to_owned(), }); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.sent_messages_photo.push(SentMessagePhoto { + lock.responses.sent_messages.push(message.clone()); + lock.responses.sent_messages_photo.push(SentMessagePhoto { message: message.clone(), bot_request: body, }); @@ -77,8 +90,9 @@ pub struct SendMessagePhotoBody { pub link_preview_options: Option, pub disable_notification: Option, pub protect_content: Option, + pub show_caption_above_media: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } diff --git a/teloxide_tests/src/server/routes/send_poll.rs b/teloxide_tests/src/server/routes/send_poll.rs index 3a0a626..c7fb734 100644 --- a/teloxide_tests/src/server/routes/send_poll.rs +++ b/teloxide_tests/src/server/routes/send_poll.rs @@ -1,14 +1,19 @@ -use crate::server::{SentMessagePoll, MESSAGES, RESPONSES}; -use crate::MockMessagePoll; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; +use std::sync::Mutex; + +use actix_web::{error::ErrorBadRequest, web, Responder}; use chrono::DateTime; use serde::Deserialize; -use teloxide::types::{Me, MessageEntity, ParseMode, PollOption, PollType, ReplyMarkup, ReplyParameters, Seconds}; - -use crate::server::routes::check_if_message_exists; +use teloxide::types::{ + BusinessConnectionId, InputPollOption, Me, MessageEntity, ParseMode, PollOption, PollType, + ReplyMarkup, ReplyParameters, Seconds, +}; use super::{make_telegram_result, BodyChatId}; +use crate::{ + server::{routes::check_if_message_exists, SentMessagePoll}, + state::State, + MockMessagePoll, +}; #[derive(Debug, Deserialize, Clone)] pub struct SendMessagePollBody { @@ -17,7 +22,7 @@ pub struct SendMessagePollBody { pub question: String, pub question_parse_mode: Option, pub question_entities: Option>, - pub options: Vec, + pub options: Vec, pub is_anonymous: Option, pub r#type: Option, pub allows_multiple_answers: Option, @@ -31,23 +36,30 @@ pub struct SendMessagePollBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } -pub async fn send_poll(body: web::Json, me: web::Data) -> impl Responder { +pub async fn send_poll( + state: web::Data>, + body: web::Json, + me: web::Data, +) -> impl Responder { + let mut lock = state.lock().unwrap(); let chat = body.chat_id.chat(); let mut message = // Creates the message, which will be mutated to fit the needed shape MockMessagePoll::new().chat(chat); message.from = Some(me.user.clone()); message.has_protected_content = body.protect_content.unwrap_or(false); + message.business_connection_id = body.business_connection_id.clone(); message.question = body.question.clone(); let mut options = vec![]; for option in body.options.iter() { options.push(PollOption { - text: option.clone(), + text: option.text.clone(), + text_entities: None, voter_count: 0, }); } @@ -60,22 +72,26 @@ pub async fn send_poll(body: web::Json, me: web::Data) message.explanation_entities = body.explanation_entities.clone(); message.open_period = body.open_period; message.close_date = DateTime::from_timestamp(body.close_date.unwrap_or(0) as i64, 0); + message.effect_id = body.message_effect_id.clone(); + message.question_entities = body.question_entities.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.sent_messages_poll.push(SentMessagePoll { + lock.responses.sent_messages.push(message.clone()); + lock.responses.sent_messages_poll.push(SentMessagePoll { message: message.clone(), bot_request: body.into_inner(), }); diff --git a/teloxide_tests/src/server/routes/send_sticker.rs b/teloxide_tests/src/server/routes/send_sticker.rs index 19f10fa..4a04983 100644 --- a/teloxide_tests/src/server/routes/send_sticker.rs +++ b/teloxide_tests/src/server/routes/send_sticker.rs @@ -1,22 +1,28 @@ -use crate::server::routes::Attachment; -use crate::server::routes::{FileType, SerializeRawFields}; -use crate::server::SentMessageSticker; -use crate::MockMessageSticker; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Mutex}; -use crate::proc_macros::SerializeRawFields; use actix_multipart::Multipart; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; +use actix_web::{error::ErrorBadRequest, web, Responder}; use serde::Deserialize; -use teloxide::types::{Me, ReplyMarkup, ReplyParameters}; - -use crate::server::{routes::check_if_message_exists, FILES, MESSAGES, RESPONSES}; +use teloxide::types::{BusinessConnectionId, Me, ReplyMarkup, ReplyParameters}; use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; +use crate::{ + proc_macros::SerializeRawFields, + server::{ + routes::{check_if_message_exists, Attachment, FileType, SerializeRawFields}, + SentMessageSticker, + }, + state::State, + MockMessageSticker, +}; -pub async fn send_sticker(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_sticker( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMessageStickerBody::serialize_raw_fields(&fields, &attachments, FileType::Sticker) .unwrap(); @@ -26,29 +32,33 @@ pub async fn send_sticker(mut payload: Multipart, me: web::Data) -> impl Res message.from = Some(me.user.clone()); message.has_protected_content = body.protect_content.unwrap_or(false); message.emoji = body.emoji.clone(); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); // Idk how to get sticker kind and sticker format from this, sooooooooooo im not doing it, // ain't nobody testing that if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.sticker().unwrap().file.clone(), path: body.file_name.to_owned(), }); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock + lock.responses.sent_messages.push(message.clone()); + lock.responses .sent_messages_sticker .push(SentMessageSticker { message: message.clone(), @@ -68,7 +78,7 @@ pub struct SendMessageStickerBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } diff --git a/teloxide_tests/src/server/routes/send_venue.rs b/teloxide_tests/src/server/routes/send_venue.rs index 86a140a..af6b809 100644 --- a/teloxide_tests/src/server/routes/send_venue.rs +++ b/teloxide_tests/src/server/routes/send_venue.rs @@ -1,13 +1,15 @@ -use crate::server::{SentMessageVenue, MESSAGES, RESPONSES}; -use crate::{MockLocation, MockMessageVenue}; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; -use teloxide::types::{Me, ReplyMarkup, ReplyParameters}; +use std::sync::Mutex; -use crate::server::routes::check_if_message_exists; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; +use teloxide::types::{BusinessConnectionId, Me, ReplyMarkup, ReplyParameters}; use super::{make_telegram_result, BodyChatId}; +use crate::{ + server::{routes::check_if_message_exists, SentMessageVenue}, + state::State, + MockLocation, MockMessageVenue, +}; #[derive(Debug, Deserialize, Clone)] pub struct SendMessageVenueBody { @@ -24,15 +26,17 @@ pub struct SendMessageVenueBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } pub async fn send_venue( body: web::Json, me: web::Data, + state: web::Data>, ) -> impl Responder { + let mut lock = state.lock().unwrap(); let chat = body.chat_id.chat(); let mut message = // Creates the message, which will be mutated to fit the needed shape MockMessageVenue::new().chat(chat); @@ -48,22 +52,26 @@ pub async fn send_venue( message.foursquare_type = body.foursquare_type.clone(); message.google_place_id = body.google_place_id.clone(); message.google_place_type = body.google_place_type.clone(); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.sent_messages_venue.push(SentMessageVenue { + lock.responses.sent_messages.push(message.clone()); + lock.responses.sent_messages_venue.push(SentMessageVenue { message: message.clone(), bot_request: body.into_inner(), }); diff --git a/teloxide_tests/src/server/routes/send_video.rs b/teloxide_tests/src/server/routes/send_video.rs index 78bdba0..efe8c10 100644 --- a/teloxide_tests/src/server/routes/send_video.rs +++ b/teloxide_tests/src/server/routes/send_video.rs @@ -1,26 +1,32 @@ -use crate::server::routes::Attachment; -use crate::server::routes::{FileType, SerializeRawFields}; -use std::collections::HashMap; -use std::str::FromStr; +use std::{collections::HashMap, str::FromStr, sync::Mutex}; -use crate::dataset::{MockMessageVideo, MockVideo}; -use crate::proc_macros::SerializeRawFields; use actix_multipart::Multipart; -use actix_web::Responder; -use actix_web::{error::ErrorBadRequest, web}; +use actix_web::{error::ErrorBadRequest, web, Responder}; use mime::Mime; -use rand::distributions::{Alphanumeric, DistString}; +use rand::distr::{Alphanumeric, SampleString}; use serde::Deserialize; -use teloxide::types::{Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, Seconds}; - -use crate::server::{ - routes::check_if_message_exists, SentMessageVideo, FILES, MESSAGES, RESPONSES, +use teloxide::types::{ + BusinessConnectionId, Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, Seconds, }; use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; +use crate::{ + dataset::{MockMessageVideo, MockVideo}, + proc_macros::SerializeRawFields, + server::{ + routes::{check_if_message_exists, Attachment, FileType, SerializeRawFields}, + SentMessageVideo, + }, + state::State, +}; -pub async fn send_video(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_video( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMessageVideoBody::serialize_raw_fields(&fields, &attachments, FileType::Video).unwrap(); let chat = body.chat_id.chat(); @@ -30,10 +36,16 @@ pub async fn send_video(mut payload: Multipart, me: web::Data) -> impl Respo message.has_protected_content = body.protect_content.unwrap_or(false); message.caption = body.caption.clone(); message.caption_entities = body.caption_entities.clone().unwrap_or_default(); + message.show_caption_above_media = body.show_caption_above_media.unwrap_or(false); + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } @@ -41,8 +53,8 @@ pub async fn send_video(mut payload: Multipart, me: web::Data) -> impl Respo message.reply_markup = Some(markup); } - let file_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); - let file_unique_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); + let file_id = Alphanumeric.sample_string(&mut rand::rng(), 16); + let file_unique_id = Alphanumeric.sample_string(&mut rand::rng(), 8); message.video = MockVideo::new() .file_id(file_id.clone()) @@ -55,16 +67,15 @@ pub async fn send_video(mut payload: Multipart, me: web::Data) -> impl Respo .mime_type(Mime::from_str("video/mp4").unwrap()) .build(); - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.video().unwrap().file.clone(), path: body.file_name.to_owned(), }); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.sent_messages_video.push(SentMessageVideo { + lock.responses.sent_messages.push(message.clone()); + lock.responses.sent_messages_video.push(SentMessageVideo { message: message.clone(), bot_request: body, }); @@ -90,7 +101,7 @@ pub struct SendMessageVideoBody { pub disable_notification: Option, pub protect_content: Option, pub message_effect_id: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, pub reply_parameters: Option, + pub business_connection_id: Option, } diff --git a/teloxide_tests/src/server/routes/send_video_note.rs b/teloxide_tests/src/server/routes/send_video_note.rs index 9dd50b3..3104812 100644 --- a/teloxide_tests/src/server/routes/send_video_note.rs +++ b/teloxide_tests/src/server/routes/send_video_note.rs @@ -1,27 +1,29 @@ -use crate::server::routes::Attachment; +use std::{collections::HashMap, sync::Mutex}; + +use actix_multipart::Multipart; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use rand::distr::{Alphanumeric, SampleString}; +use serde::Deserialize; +use teloxide::types::{BusinessConnectionId, Me, ReplyMarkup, ReplyParameters, Seconds}; + +use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; use crate::{ + proc_macros::SerializeRawFields, server::{ - routes::{FileType, SerializeRawFields}, + routes::{check_if_message_exists, Attachment, FileType, SerializeRawFields}, SentMessageVideoNote, }, + state::State, MockMessageVideoNote, }; -use std::collections::HashMap; - -use crate::proc_macros::SerializeRawFields; -use actix_multipart::Multipart; -use actix_web::Responder; -use actix_web::{error::ErrorBadRequest, web}; -use rand::distributions::{Alphanumeric, DistString}; -use serde::Deserialize; -use teloxide::types::{Me, ReplyMarkup, ReplyParameters, Seconds}; - -use crate::server::{routes::check_if_message_exists, FILES, MESSAGES, RESPONSES}; -use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; - -pub async fn send_video_note(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_video_note( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMessageVideoNoteBody::serialize_raw_fields(&fields, &attachments, FileType::Voice) .unwrap(); @@ -32,33 +34,37 @@ pub async fn send_video_note(mut payload: Multipart, me: web::Data) -> impl message.has_protected_content = body.protect_content.unwrap_or(false); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let file_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); - let file_unique_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); + let file_id = Alphanumeric.sample_string(&mut rand::rng(), 16); + let file_unique_id = Alphanumeric.sample_string(&mut rand::rng(), 8); message.file_id = file_id.clone(); message.file_unique_id = file_unique_id.clone(); message.duration = body.duration.unwrap_or(Seconds::from_seconds(0)); message.length = body.length.unwrap_or(100); message.file_size = body.file_data.bytes().len() as u32; + message.effect_id = body.message_effect_id.clone(); + message.business_connection_id = body.business_connection_id.clone(); - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.video_note().unwrap().file.clone(), path: body.file_name.to_owned(), }); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock + lock.responses.sent_messages.push(message.clone()); + lock.responses .sent_messages_video_note .push(SentMessageVideoNote { message: message.clone(), @@ -80,6 +86,6 @@ pub struct SendMessageVideoNoteBody { pub protect_content: Option, pub message_effect_id: Option, pub reply_parameters: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, + pub business_connection_id: Option, } diff --git a/teloxide_tests/src/server/routes/send_voice.rs b/teloxide_tests/src/server/routes/send_voice.rs index 0a398e7..15a764e 100644 --- a/teloxide_tests/src/server/routes/send_voice.rs +++ b/teloxide_tests/src/server/routes/send_voice.rs @@ -1,28 +1,32 @@ -use crate::server::routes::Attachment; -use crate::{ - server::{ - routes::{FileType, SerializeRawFields}, - SentMessageVoice, - }, - MockMessageVoice, -}; -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, str::FromStr, sync::Mutex}; -use crate::proc_macros::SerializeRawFields; use actix_multipart::Multipart; -use actix_web::Responder; -use actix_web::{error::ErrorBadRequest, web}; +use actix_web::{error::ErrorBadRequest, web, Responder}; use mime::Mime; -use rand::distributions::{Alphanumeric, DistString}; +use rand::distr::{Alphanumeric, SampleString}; use serde::Deserialize; -use teloxide::types::{Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, Seconds}; - -use crate::server::{routes::check_if_message_exists, FILES, MESSAGES, RESPONSES}; +use teloxide::types::{ + BusinessConnectionId, Me, MessageEntity, ParseMode, ReplyMarkup, ReplyParameters, Seconds, +}; use super::{get_raw_multipart_fields, make_telegram_result, BodyChatId}; +use crate::{ + proc_macros::SerializeRawFields, + server::{ + routes::{check_if_message_exists, Attachment, FileType, SerializeRawFields}, + SentMessageVoice, + }, + state::State, + MockMessageVoice, +}; -pub async fn send_voice(mut payload: Multipart, me: web::Data) -> impl Responder { +pub async fn send_voice( + mut payload: Multipart, + me: web::Data, + state: web::Data>, +) -> impl Responder { let (fields, attachments) = get_raw_multipart_fields(&mut payload).await; + let mut lock = state.lock().unwrap(); let body = SendMessageVoiceBody::serialize_raw_fields(&fields, &attachments, FileType::Voice).unwrap(); let chat = body.chat_id.chat(); @@ -32,35 +36,39 @@ pub async fn send_voice(mut payload: Multipart, me: web::Data) -> impl Respo message.has_protected_content = body.protect_content.unwrap_or(false); message.caption = body.caption.clone(); message.caption_entities = body.caption_entities.clone().unwrap_or_default(); + message.business_connection_id = body.business_connection_id.clone(); if let Some(reply_parameters) = &body.reply_parameters { - check_if_message_exists!(reply_parameters.message_id.0); - let reply_to_message = MESSAGES.get_message(reply_parameters.message_id.0).unwrap(); + check_if_message_exists!(lock, reply_parameters.message_id.0); + let reply_to_message = lock + .messages + .get_message(reply_parameters.message_id.0) + .unwrap(); message.reply_to_message = Some(Box::new(reply_to_message.clone())); } if let Some(ReplyMarkup::InlineKeyboard(markup)) = body.reply_markup.clone() { message.reply_markup = Some(markup); } - let file_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); - let file_unique_id = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); + let file_id = Alphanumeric.sample_string(&mut rand::rng(), 16); + let file_unique_id = Alphanumeric.sample_string(&mut rand::rng(), 8); message.file_id = file_id.clone(); message.file_unique_id = file_unique_id.clone(); message.duration = body.duration.unwrap_or(Seconds::from_seconds(0)); message.file_size = body.file_data.bytes().len() as u32; message.mime_type = Some(Mime::from_str("audio/mp3").unwrap()); + message.effect_id = body.message_effect_id.clone(); - let last_id = MESSAGES.max_message_id(); - let message = MESSAGES.add_message(message.id(last_id + 1).build()); + let last_id = lock.messages.max_message_id(); + let message = lock.messages.add_message(message.id(last_id + 1).build()); - FILES.lock().unwrap().push(teloxide::types::File { + lock.files.push(teloxide::types::File { meta: message.voice().unwrap().file.clone(), path: body.file_name.to_owned(), }); - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.sent_messages.push(message.clone()); - responses_lock.sent_messages_voice.push(SentMessageVoice { + lock.responses.sent_messages.push(message.clone()); + lock.responses.sent_messages_voice.push(SentMessageVoice { message: message.clone(), bot_request: body, }); @@ -82,6 +90,6 @@ pub struct SendMessageVoiceBody { pub protect_content: Option, pub message_effect_id: Option, pub reply_parameters: Option, - #[serde(default, with = "crate::server::routes::reply_markup_deserialize")] pub reply_markup: Option, + pub business_connection_id: Option, } diff --git a/teloxide_tests/src/server/routes/set_message_reaction.rs b/teloxide_tests/src/server/routes/set_message_reaction.rs index fdebfe5..430a670 100644 --- a/teloxide_tests/src/server/routes/set_message_reaction.rs +++ b/teloxide_tests/src/server/routes/set_message_reaction.rs @@ -1,14 +1,11 @@ -use crate::server::SetMessageReaction; -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; -use teloxide::types:: - ReactionType -; +use std::sync::Mutex; -use crate::server::{routes::check_if_message_exists, MESSAGES, RESPONSES}; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; +use teloxide::types::ReactionType; use super::{make_telegram_result, BodyChatId}; +use crate::{server::routes::check_if_message_exists, state::State}; #[derive(Debug, Deserialize, Clone)] pub struct SetMessageReactionBody { @@ -19,15 +16,14 @@ pub struct SetMessageReactionBody { } pub async fn set_message_reaction( + state: web::Data>, body: web::Json, ) -> impl Responder { - check_if_message_exists!(body.message_id); - - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.set_message_reaction.push(SetMessageReaction { - bot_request: body.into_inner(), - }); + let mut lock = state.lock().unwrap(); + + check_if_message_exists!(lock, body.message_id); + + lock.responses.set_message_reaction.push(body.into_inner()); make_telegram_result(true) } - diff --git a/teloxide_tests/src/server/routes/set_my_commands.rs b/teloxide_tests/src/server/routes/set_my_commands.rs new file mode 100644 index 0000000..4904bad --- /dev/null +++ b/teloxide_tests/src/server/routes/set_my_commands.rs @@ -0,0 +1,25 @@ +use std::sync::Mutex; + +use actix_web::{web, Responder}; +use serde::Deserialize; +use teloxide::types::{BotCommand, BotCommandScope}; + +use super::make_telegram_result; +use crate::state::State; + +#[derive(Debug, Deserialize, Clone)] +pub struct SetMyCommandsBody { + pub commands: Vec, + pub scope: Option, + pub language_code: Option, +} + +pub async fn set_my_commands( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); + lock.responses.set_my_commands.push(body.into_inner()); + + make_telegram_result(true) +} diff --git a/teloxide_tests/src/server/routes/unban_chat_member.rs b/teloxide_tests/src/server/routes/unban_chat_member.rs index d10e734..a9236ab 100644 --- a/teloxide_tests/src/server/routes/unban_chat_member.rs +++ b/teloxide_tests/src/server/routes/unban_chat_member.rs @@ -1,10 +1,10 @@ +use std::sync::Mutex; + use actix_web::{web, Responder}; use serde::Deserialize; -use crate::server::routes::make_telegram_result; -use crate::server::RESPONSES; - use super::BodyChatId; +use crate::{server::routes::make_telegram_result, state::State}; #[derive(Debug, Deserialize, Clone)] pub struct UnbanChatMemberBody { @@ -13,10 +13,13 @@ pub struct UnbanChatMemberBody { pub only_if_banned: Option, } -pub async fn unban_chat_member(body: web::Json) -> impl Responder { +pub async fn unban_chat_member( + state: web::Data>, + body: web::Json, +) -> impl Responder { // Idk what to verify here - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock.unbanned_chat_members.push(body.into_inner()); + let mut lock = state.lock().unwrap(); + lock.responses.unbanned_chat_members.push(body.into_inner()); make_telegram_result(true) } diff --git a/teloxide_tests/src/server/routes/unpin_all_chat_messages.rs b/teloxide_tests/src/server/routes/unpin_all_chat_messages.rs index 5cf2e52..aa3196b 100644 --- a/teloxide_tests/src/server/routes/unpin_all_chat_messages.rs +++ b/teloxide_tests/src/server/routes/unpin_all_chat_messages.rs @@ -1,19 +1,22 @@ +use std::sync::Mutex; + use actix_web::{web, Responder}; use serde::Deserialize; -use crate::server::routes::make_telegram_result; -use crate::server::RESPONSES; - use super::BodyChatId; +use crate::{server::routes::make_telegram_result, state::State}; #[derive(Debug, Deserialize, Clone)] pub struct UnpinAllChatMessagesBody { pub chat_id: BodyChatId, } -pub async fn unpin_all_chat_messages(body: web::Json) -> impl Responder { - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock +pub async fn unpin_all_chat_messages( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); + lock.responses .unpinned_all_chat_messages .push(body.into_inner()); diff --git a/teloxide_tests/src/server/routes/unpin_chat_message.rs b/teloxide_tests/src/server/routes/unpin_chat_message.rs index c072e0c..5bdb1a6 100644 --- a/teloxide_tests/src/server/routes/unpin_chat_message.rs +++ b/teloxide_tests/src/server/routes/unpin_chat_message.rs @@ -1,24 +1,28 @@ -use actix_web::error::ErrorBadRequest; -use actix_web::{web, Responder}; -use serde::Deserialize; +use std::sync::Mutex; -use crate::server::routes::make_telegram_result; -use crate::server::{MESSAGES, RESPONSES}; +use actix_web::{error::ErrorBadRequest, web, Responder}; +use serde::Deserialize; +use teloxide::types::BusinessConnectionId; use super::{check_if_message_exists, BodyChatId}; +use crate::{server::routes::make_telegram_result, state::State}; #[derive(Debug, Deserialize, Clone)] pub struct UnpinChatMessageBody { pub chat_id: BodyChatId, pub message_id: Option, + pub business_connection_id: Option, } -pub async fn unpin_chat_message(body: web::Json) -> impl Responder { +pub async fn unpin_chat_message( + state: web::Data>, + body: web::Json, +) -> impl Responder { + let mut lock = state.lock().unwrap(); if let Some(message_id) = body.message_id { - check_if_message_exists!(message_id); + check_if_message_exists!(lock, message_id); } - let mut responses_lock = RESPONSES.lock().unwrap(); - responses_lock + lock.responses .unpinned_chat_messages .push(body.into_inner()); diff --git a/teloxide_tests/src/state.rs b/teloxide_tests/src/state.rs new file mode 100644 index 0000000..73a279b --- /dev/null +++ b/teloxide_tests/src/state.rs @@ -0,0 +1,81 @@ +use teloxide::{ + prelude::*, + types::{File, MessageId, MessageKind}, +}; + +use crate::{server::messages::Messages, utils::find_file, MockMessageText, Responses}; + +#[derive(Default)] +pub(crate) struct State { + pub files: Vec, + pub responses: Responses, + pub messages: Messages, +} + +impl State { + pub fn reset(&mut self) { + self.responses = Responses::default(); + } + + pub(crate) fn add_message(&mut self, message: &mut Message) { + let max_id = self.messages.max_message_id(); + let maybe_message = self.messages.get_message(message.id.0); + + // If message exists in the database, and it isn't a default, + // let it be, the user knows best + if maybe_message.is_some() && message.id != MessageId(MockMessageText::ID) { + log::debug!( + "Not inserting message with id {}, this id exists in the database.", + message.id + ); + return; + } + + if message.id.0 <= max_id || maybe_message.is_some() { + message.id = MessageId(max_id + 1); + } + + if let Some(file_meta) = find_file(serde_json::to_value(&message).unwrap()) { + let file = File { + meta: file_meta, + path: "some_path.txt".to_string(), // This doesn't really matter + }; + self.files.push(file); + } + if let MessageKind::Common(ref mut message_kind) = message.kind { + if let Some(ref mut reply_message) = message_kind.reply_to_message { + self.add_message(reply_message); + } + } + log::debug!("Inserted message with {}.", message.id); + self.messages.add_message(message.clone()); + } + + pub(crate) fn edit_message(&mut self, message: &mut Message) { + let old_message = self.messages.get_message(message.id.0); + + if old_message.is_none() { + log::error!( + "Not editing message with id {}, this id does not exist in the database.", + message.id + ); + return; + } + + if let Some(file_meta) = find_file(serde_json::to_value(&message).unwrap()) { + if self + .files + .iter() + .all(|f| f.meta.unique_id != file_meta.unique_id) + { + let file = File { + meta: file_meta, + path: "some_path.txt".to_string(), // This doesn't really matter + }; + self.files.push(file); + } + } + log::debug!("Edited message with {}.", message.id); + self.messages.edit_message(message.clone()); + } +} diff --git a/teloxide_tests/src/tests.rs b/teloxide_tests/src/tests.rs index 9ab9d89..07b7134 100644 --- a/teloxide_tests/src/tests.rs +++ b/teloxide_tests/src/tests.rs @@ -1,29 +1,36 @@ -use super::*; -use crate::dataset::*; -use serde::{Deserialize, Serialize}; -use teloxide::dispatching::{HandlerExt, UpdateHandler}; -use teloxide::dptree::case; -use teloxide::net::Download; -use teloxide::payloads::{ - BanChatMemberSetters, CopyMessageSetters, SendPhotoSetters, SendPollSetters, -}; -use teloxide::requests::Requester; -use teloxide::types::{ - ChatAction, ChatPermissions, DiceEmoji, InlineKeyboardButton, InlineKeyboardMarkup, InputFile, - InputMedia, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, - LinkPreviewOptions, Message, MessageEntity, PollOption, PollType, ReactionType, - ReplyParameters, Seconds, Update, +use std::{ + fmt::Display, + sync::{Arc, RwLock}, + thread, }; + +use chrono::Utc; +use futures_util::future::BoxFuture; +use serde::{Deserialize, Serialize}; use teloxide::{ dispatching::{ - dialogue::{self, InMemStorage}, - UpdateFilterExt, + dialogue::{self, serializer::Json, ErasedStorage, InMemStorage, SqliteStorage, Storage}, + HandlerExt, UpdateFilterExt, UpdateHandler, }, - dptree::deps, + dptree::{case, deps}, + error_handlers::ErrorHandler, macros::BotCommands, + net::Download, + payloads::{BanChatMemberSetters, CopyMessageSetters, SendPhotoSetters, SendPollSetters}, prelude::*, + requests::Requester, + sugar::request::RequestReplyExt, + types::{ + BotCommand, ChatAction, ChatPermissions, DiceEmoji, InlineKeyboardButton, + InlineKeyboardMarkup, InputFile, InputMedia, InputMediaAudio, InputMediaDocument, + InputMediaPhoto, InputMediaVideo, LabeledPrice, LinkPreviewOptions, Message, MessageEntity, + MessageId, PollOption, PollType, ReactionType, ReplyParameters, Update, + }, }; +use super::*; +use crate::dataset::*; + // // // @@ -35,12 +42,20 @@ enum State { NotStart, } +type MyDialogue = Dialogue>; +type ErasedDialogue = Dialogue>; +type MyStorage = Arc>; + async fn handler_with_state( bot: Bot, dialogue: MyDialogue, msg: Message, ) -> Result<(), Box> { bot.send_message(msg.chat.id, msg.text().unwrap()).await?; + if msg.text().unwrap() == "exit" { + dialogue.exit().await?; + return Ok(()); + } dialogue.update(State::NotStart).await?; Ok(()) @@ -67,7 +82,7 @@ fn get_dialogue_schema() -> UpdateHandler::new(); bot.dependencies(deps![storage]); bot.set_state(State::Start).await; @@ -81,9 +96,40 @@ async fn test_echo_with_start_state() { assert_eq!(last_response.text(), Some("test")); } +#[tokio::test] +async fn test_assert_state() { + let mut bot = MockBot::new(MockMessageText::new().text("test"), get_dialogue_schema()); + let storage = InMemStorage::::new(); + bot.dependencies(deps![storage]); + bot.set_state(State::Start).await; + + bot.dispatch().await; + + bot.assert_state(State::NotStart).await; + + let last_response = bot.get_responses().sent_messages.pop().unwrap(); + assert_eq!(last_response.text(), Some("test")); +} + +#[tokio::test] +async fn test_try_get() { + let mut bot = MockBot::new(MockMessageText::new().text("exit"), get_dialogue_schema()); + let storage = InMemStorage::::new(); + bot.dependencies(deps![storage]); + bot.set_state(State::Start).await; + + bot.dispatch().await; + + let last_response = bot.get_responses().sent_messages.pop().unwrap(); + let state: Option = bot.try_get_state().await; + assert_eq!(state, None); + + assert_eq!(last_response.text(), Some("exit")); +} + #[tokio::test] async fn test_echo_with_not_start_test() { - let bot = MockBot::new(MockMessageText::new().text("test"), get_dialogue_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("test"), get_dialogue_schema()); let storage = InMemStorage::::new(); bot.dependencies(deps![storage]); bot.set_state(State::NotStart).await; @@ -97,6 +143,36 @@ async fn test_echo_with_not_start_test() { assert_eq!(last_response.text(), Some("Not start!")); } +fn get_erased_dialogue_schema() -> UpdateHandler> +{ + dialogue::enter::, State, _>() + .branch(Update::filter_message().endpoint(handler_with_erased_state)) +} + +async fn handler_with_erased_state( + bot: Bot, + dialogue: ErasedDialogue, + msg: Message, +) -> Result<(), Box> { + bot.send_message(msg.chat.id, msg.text().unwrap()).await?; + dialogue.update(State::NotStart).await?; + Ok(()) +} + +#[tokio::test] +async fn test_erased_state() { + let mut bot = MockBot::new( + MockMessageText::new().text("test"), + get_erased_dialogue_schema(), + ); + let storage: MyStorage = SqliteStorage::open(":memory:", Json).await.unwrap().erase(); + bot.dependencies(deps![storage]); + + // This .dispatch is important?.. + bot.dispatch().await; + bot.dispatch_and_check_state(State::NotStart).await; +} + // // // @@ -109,8 +185,12 @@ pub enum AllCommands { #[command()] Edit, #[command()] + EditUnchanged, + #[command()] Delete, #[command()] + DeleteBatch, + #[command()] EditReplyMarkup, #[command()] Photo, @@ -141,6 +221,8 @@ pub enum AllCommands { #[command()] MediaGroup, #[command()] + Invoice, + #[command()] EditCaption, #[command()] PinMessage, @@ -156,10 +238,12 @@ pub enum AllCommands { ChatAction, #[command()] SetMessageReaction, + #[command()] + SetMyCommands, + #[command()] + Panic, } -type MyDialogue = Dialogue>; - async fn handler( bot: Bot, msg: Message, @@ -182,9 +266,18 @@ async fn handler( .link_preview_options(link_preview_options) .await?; } + AllCommands::EditUnchanged => { + bot.edit_message_text(msg.chat.id, sent_message.id, msg.text().unwrap()) + .link_preview_options(link_preview_options) + .await?; + } AllCommands::Delete => { bot.delete_message(msg.chat.id, sent_message.id).await?; } + AllCommands::DeleteBatch => { + bot.delete_messages(msg.chat.id, vec![sent_message.id, MessageId(404)]) + .await?; + } AllCommands::EditReplyMarkup => { bot.edit_message_reply_markup(msg.chat.id, sent_message.id) .reply_markup(InlineKeyboardMarkup::new(vec![vec![ @@ -265,7 +358,7 @@ async fn handler( } AllCommands::Location => { bot.send_location(msg.chat.id, 1.0, 1.0) - .live_period(60) + .live_period(60.into()) .reply_parameters(reply_options) .await?; } @@ -286,7 +379,7 @@ async fn handler( bot.send_poll( msg.chat.id, "what is test", - vec!["test".to_string(), "not test".to_string()], + vec!["test".to_string().into(), "not test".to_string().into()], ) .type_(PollType::Quiz) .reply_parameters(reply_options) @@ -385,6 +478,33 @@ async fn handler( }]) .await?; } + AllCommands::SetMyCommands => { + bot.set_my_commands(vec![BotCommand { + command: String::from("test"), + description: String::from("test"), + }]) + .await?; + } + AllCommands::Panic => { + // This message id does not exist + bot.send_message(msg.chat.id, "test") + .reply_to(MessageId(344382918)) + .await?; + } + AllCommands::Invoice => { + bot.send_invoice( + msg.chat.id, + "Absolutely Nothing", + "Demo", + "test_payload", + "XTR", + vec![LabeledPrice { + label: "Stars".into(), + amount: 1, + }], + ) + .await?; + } } Ok(()) } @@ -406,13 +526,17 @@ fn get_schema() -> UpdateHandler() .endpoint(handler), ) - .branch(Update::filter_message().endpoint(handler)) + .branch( + Update::filter_edited_message() + .filter_command::() + .branch(case![AllCommands::ForwardMessage].endpoint(handler)), + ) .branch(Update::filter_callback_query().endpoint(callback_handler)) } #[tokio::test] async fn test_echo() { - let bot = MockBot::new(MockMessageText::new().text("/echo echo"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/echo echo"), get_schema()); bot.dispatch().await; @@ -424,22 +548,86 @@ async fn test_echo() { #[tokio::test] #[should_panic] async fn test_panic() { - // Nothing else should fail because it panics - let bot = MockBot::new(MockMessageText::new().text("/echo echo"), get_schema()); - - bot.dispatch().await; + // Nothing else should fail because this panics + let bot = MockBot::new(MockMessageText::new().text("/panic"), get_schema()); - let last_response = bot.get_responses().sent_messages.pop().unwrap(); - if last_response.text() == Some("/echo echo") { - panic!("panic!"); + // To actually keep the bot in scope + if true { + panic!("Expected panic"); } drop(bot); } +pub struct MyErrorHandler { + errors: Arc>>, +} + +impl MyErrorHandler { + pub fn new() -> Self { + Self { + errors: Arc::new(RwLock::new(vec![])), + } + } + + pub fn errors(&self) -> Vec { + self.errors.read().unwrap().clone() + } +} + +impl ErrorHandler for MyErrorHandler +where + E: std::fmt::Debug + Display + 'static + Sync + Send, +{ + fn handle_error(self: Arc, error: E) -> BoxFuture<'static, ()> { + thread::spawn(|| { + respond_to_error(); + }) + .join() + .unwrap(); + + self.errors.write().unwrap().push(format!("{error:?}")); + Box::pin(async {}) + } +} + +#[tokio::main] +async fn respond_to_error() { + let bot = Bot::from_env(); + bot.send_message(ChatId(MockUser::ID as i64), "Error detected!") + .await + .unwrap(); +} + +#[tokio::test] +async fn test_error_handler() { + let mut bot = MockBot::new(MockMessageText::new().text("/panic"), get_schema()); + let error_handler = Arc::new(MyErrorHandler::new()); + bot.error_handler(error_handler.clone()); + + bot.dispatch_and_check_last_text("Error detected!").await; + + let errors = error_handler.errors(); + assert_eq!(errors.len(), 1); + assert!(errors[0].contains("Message not found")); +} + +#[tokio::test] +async fn test_no_updates() { + let empty: Vec = vec![]; + let mut bot = MockBot::new(empty, get_schema()); + + // This shouldn't panic + bot.dispatch().await; + + // Just to test that everything is fine + bot.update(MockMessageText::new().text("/echo echo")); + bot.dispatch_and_check_last_text("/echo echo").await; +} + #[tokio::test] async fn test_send_photo() { - let bot = MockBot::new(MockMessageText::new().text("/photo"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/photo"), get_schema()); bot.dispatch().await; @@ -457,7 +645,7 @@ async fn test_send_photo() { #[tokio::test] async fn test_send_video() { - let bot = MockBot::new(MockMessageText::new().text("/video"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/video"), get_schema()); bot.dispatch().await; @@ -475,7 +663,7 @@ async fn test_send_video() { #[tokio::test] async fn test_send_audio() { - let bot = MockBot::new(MockMessageText::new().text("/audio"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/audio"), get_schema()); bot.dispatch().await; @@ -493,7 +681,7 @@ async fn test_send_audio() { #[tokio::test] async fn test_send_voice() { - let bot = MockBot::new(MockMessageText::new().text("/voice"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/voice"), get_schema()); bot.dispatch().await; @@ -511,7 +699,7 @@ async fn test_send_voice() { #[tokio::test] async fn test_send_video_note() { - let bot = MockBot::new(MockMessageText::new().text("/videonote"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/videonote"), get_schema()); bot.dispatch().await; @@ -527,7 +715,7 @@ async fn test_send_video_note() { #[tokio::test] async fn test_send_document() { - let bot = MockBot::new(MockMessageText::new().text("/document"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/document"), get_schema()); bot.dispatch().await; @@ -544,7 +732,7 @@ async fn test_send_document() { #[tokio::test] async fn test_send_animation() { - let bot = MockBot::new(MockMessageText::new().text("/animation"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/animation"), get_schema()); bot.dispatch().await; @@ -559,7 +747,7 @@ async fn test_send_animation() { #[tokio::test] async fn test_send_media_group() { - let bot = MockBot::new(MockMessageText::new().text("/mediagroup"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/mediagroup"), get_schema()); bot.dispatch().await; @@ -673,7 +861,7 @@ async fn test_send_media_group() { #[tokio::test] async fn test_send_location() { - let bot = MockBot::new(MockMessageText::new().text("/location"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/location"), get_schema()); bot.dispatch().await; @@ -685,15 +873,12 @@ async fn test_send_location() { ); assert_eq!(last_sent_message.location().unwrap().latitude, 1.0); assert_eq!(last_sent_message.location().unwrap().longitude, 1.0); - assert_eq!( - last_sent_location.bot_request.live_period, - Some(Seconds::from_seconds(60)) - ); + assert_eq!(last_sent_location.bot_request.live_period, Some(60.into())); } #[tokio::test] async fn test_send_venue() { - let bot = MockBot::new(MockMessageText::new().text("/venue"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/venue"), get_schema()); bot.dispatch().await; @@ -711,7 +896,7 @@ async fn test_send_venue() { #[tokio::test] async fn test_send_contact() { - let bot = MockBot::new(MockMessageText::new().text("/contact"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/contact"), get_schema()); bot.dispatch().await; @@ -730,7 +915,7 @@ async fn test_send_contact() { #[tokio::test] async fn test_send_dice() { - let bot = MockBot::new(MockMessageText::new().text("/dice"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/dice"), get_schema()); bot.dispatch().await; @@ -742,7 +927,7 @@ async fn test_send_dice() { #[tokio::test] async fn test_send_poll() { - let bot = MockBot::new(MockMessageText::new().text("/poll"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/poll"), get_schema()); bot.dispatch().await; @@ -758,10 +943,12 @@ async fn test_send_poll() { vec![ PollOption { text: "test".to_string(), + text_entities: None, voter_count: 0 }, PollOption { text: "not test".to_string(), + text_entities: None, voter_count: 0 } ], @@ -776,7 +963,7 @@ async fn test_send_poll() { #[tokio::test] async fn test_send_sticker() { - let bot = MockBot::new(MockMessageText::new().text("/sticker"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/sticker"), get_schema()); bot.dispatch().await; @@ -791,7 +978,7 @@ async fn test_send_sticker() { #[tokio::test] async fn test_edit_message() { - let bot = MockBot::new(MockMessageText::new().text("/edit"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/edit"), get_schema()); bot.dispatch().await; @@ -810,9 +997,23 @@ async fn test_edit_message() { ); } +#[tokio::test] +async fn test_edit_message_unchanged() { + let mut bot = MockBot::new(MockMessageText::new().text("/editunchanged"), get_schema()); + let error_handler = Arc::new(MyErrorHandler::new()); + bot.error_handler(error_handler.clone()); + + bot.dispatch().await; + + assert!(bot.get_responses().edited_messages_text.is_empty()); + let errors = error_handler.errors(); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0], "Api(MessageNotModified)"); +} + #[tokio::test] async fn test_edit_caption() { - let bot = MockBot::new(MockMessageText::new().text("/editcaption"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/editcaption"), get_schema()); bot.dispatch().await; @@ -825,7 +1026,7 @@ async fn test_edit_caption() { #[tokio::test] async fn test_edit_reply_markup() { - let bot = MockBot::new( + let mut bot = MockBot::new( MockMessageText::new().text("/editreplymarkup"), get_schema(), ); @@ -853,7 +1054,7 @@ async fn test_edit_reply_markup() { #[tokio::test] async fn test_delete_message() { - let bot = MockBot::new(MockMessageText::new().text("/delete"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/delete"), get_schema()); bot.dispatch().await; @@ -864,9 +1065,22 @@ async fn test_delete_message() { assert_eq!(last_deleted_response.message.id, last_sent_message.id); } +#[tokio::test] +async fn test_delete_messages() { + let mut bot = MockBot::new(MockMessageText::new().text("/deletebatch"), get_schema()); + + bot.dispatch().await; + + let last_sent_message = bot.get_responses().sent_messages.pop().unwrap(); + let last_deleted_response = bot.get_responses().deleted_messages.pop().unwrap(); + + assert_eq!(last_sent_message.text(), Some("/deletebatch")); + assert_eq!(last_deleted_response.message.id, last_sent_message.id); +} + #[tokio::test] async fn test_answer_callback_query() { - let bot = MockBot::new(MockCallbackQuery::new().data("test"), get_schema()); + let mut bot = MockBot::new(MockCallbackQuery::new().data("test"), get_schema()); bot.dispatch().await; @@ -877,7 +1091,7 @@ async fn test_answer_callback_query() { #[tokio::test] async fn test_pin_message() { - let bot = MockBot::new(MockMessageText::new().text("/pinmessage"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/pinmessage"), get_schema()); bot.dispatch().await; @@ -892,7 +1106,7 @@ async fn test_pin_message() { #[tokio::test] async fn test_forward_message() { - let bot = MockBot::new(MockMessageText::new().text("/forwardmessage"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/forwardmessage"), get_schema()); bot.dispatch().await; @@ -905,11 +1119,17 @@ async fn test_forward_message() { last_sent_message.forward_date(), Some(first_sent_message.date) ); + assert_eq!( + last_sent_message.forward_from_user().unwrap().id, + first_sent_message.from.as_ref().unwrap().id + ); + assert_eq!(responses.forwarded_messages.len(), 1); + assert_eq!(&responses.forwarded_messages[0].message, last_sent_message); } #[tokio::test] async fn test_copy_message() { - let bot = MockBot::new(MockMessageText::new().text("/copymessage"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/copymessage"), get_schema()); bot.dispatch().await; @@ -928,7 +1148,7 @@ async fn test_copy_message() { #[tokio::test] async fn test_ban_and_unban() { - let bot = MockBot::new(MockMessageText::new().text("/ban"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/ban"), get_schema()); bot.dispatch().await; @@ -942,7 +1162,7 @@ async fn test_ban_and_unban() { #[tokio::test] async fn test_restrict() { - let bot = MockBot::new(MockMessageText::new().text("/restrict"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/restrict"), get_schema()); bot.dispatch().await; @@ -955,7 +1175,7 @@ async fn test_restrict() { #[tokio::test] async fn test_send_chat_action() { - let bot = MockBot::new(MockMessageText::new().text("/chataction"), get_schema()); + let mut bot = MockBot::new(MockMessageText::new().text("/chataction"), get_schema()); bot.dispatch().await; @@ -967,7 +1187,7 @@ async fn test_send_chat_action() { #[tokio::test] async fn test_set_message_reaction() { - let bot = MockBot::new( + let mut bot = MockBot::new( MockMessageText::new().text("/setmessagereaction"), get_schema(), ); @@ -978,9 +1198,68 @@ async fn test_set_message_reaction() { let last_reaction = responses.set_message_reaction.last().unwrap(); assert_eq!( - last_reaction.bot_request.reaction.clone().unwrap()[0], + last_reaction.reaction.clone().unwrap()[0], ReactionType::Emoji { emoji: "👍".to_owned() } ); } + +#[tokio::test] +async fn test_set_my_commands() { + let mut bot = MockBot::new(MockMessageText::new().text("/setmycommands"), get_schema()); + + bot.dispatch().await; + + let responses = bot.get_responses(); + let set_commands = responses.set_my_commands.last().unwrap(); + + assert_eq!( + set_commands.commands.first(), + Some(&BotCommand { + command: String::from("test"), + description: String::from("test") + }) + ); +} + +#[tokio::test] +async fn test_send_invoice() { + let mut bot = MockBot::new(MockMessageText::new().text("/invoice"), get_schema()); + + bot.dispatch().await; + + let responses = bot.get_responses(); + let invoice_message = responses.sent_messages_invoice.last().unwrap(); + + assert_eq!( + invoice_message.message.invoice().unwrap().title, + "Absolutely Nothing" + ); +} + +#[tokio::test] +async fn test_edited_message() { + let mock_message = MockMessageText::new().text("/forwardmessage first"); + let mut bot = MockBot::new(mock_message.clone(), get_schema()); + bot.dispatch().await; + + let responses = bot.get_responses(); + assert_eq!(responses.forwarded_messages.len(), 1); + let forwarded_message = &responses.forwarded_messages[0].message; + assert_eq!(forwarded_message.text(), Some("/forwardmessage first")); + + let edited_message = MockEditedMessage::new( + mock_message + .text("/forwardmessage second") + .edit_date(Utc::now()) + .build(), + ); + bot.update(edited_message); + bot.dispatch().await; + + let responses = bot.get_responses(); + assert_eq!(responses.forwarded_messages.len(), 1); + let forwarded_message = &responses.forwarded_messages[0].message; + assert_eq!(forwarded_message.text(), Some("/forwardmessage second")); +} diff --git a/teloxide_tests/src/utils.rs b/teloxide_tests/src/utils.rs new file mode 100644 index 0000000..76906cc --- /dev/null +++ b/teloxide_tests/src/utils.rs @@ -0,0 +1,81 @@ +use serde_json::Value; +use teloxide::{prelude::*, types::FileMeta}; + +macro_rules! assert_eqn { + ($actual:expr, $expected:expr $(,)?) => { + match (&$actual, &$expected) { + (actual, expected) => { + if !(*actual == *expected) { + panic!("assertion `actual == expected` failed: + actual: {actual:?} + expected: {expected:?}", actual=&*actual, expected=&*expected) + + } + } + } + }; + ($actual:expr, $expected:expr, $($arg:tt)+) => { + match (&$actual, &$expected) { + (actual, expected) => { + if !(*actual == *expected) { + panic!("assertion `actual == expected` failed: {message} + actual: {actual:?} + expected: {expected:?}", message=$($arg)+, actual=&*actual, expected=&*expected) + + } + } + } + }; +} + +pub(crate) use assert_eqn; + +pub fn find_file(value: Value) -> Option { + // Recursively searches for file meta + let mut file_id = None; + let mut file_unique_id = None; + let mut file_size = None; + if let Value::Object(map) = value { + for (k, v) in map { + if k == "file_id" { + file_id = Some(v.as_str().unwrap().to_string()); + } else if k == "file_unique_id" { + file_unique_id = Some(v.as_str().unwrap().to_string()); + } else if k == "file_size" { + file_size = Some(v.as_u64().unwrap() as u32); + } else if let Some(found) = find_file(v) { + return Some(found); + } + } + } + if let (Some(id), Some(unique_id)) = (file_id, file_unique_id) { + return Some(FileMeta { + id, + unique_id, + size: file_size.unwrap_or(0), + }); + } + None +} + +pub fn find_chat_id(value: Value) -> Option { + // Recursively searches for chat id + if let Value::Object(map) = value { + for (k, v) in map { + if k == "chat" { + return v["id"].as_i64(); + } else if let Some(found) = find_chat_id(v) { + return Some(found); + } + } + } + None +} + +/// A key that defines the parallelism of updates +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +pub struct DistributionKey(pub ChatId); + +pub(crate) fn default_distribution_function(update: &Update) -> Option { + update.chat().map(|c| c.id).map(DistributionKey) +} diff --git a/teloxide_tests_macros/src/lib.rs b/teloxide_tests_macros/src/lib.rs index 10fb338..2479b77 100644 --- a/teloxide_tests_macros/src/lib.rs +++ b/teloxide_tests_macros/src/lib.rs @@ -1,4 +1,8 @@ //! Proc macros for teloxide_tests crate + +#![allow(clippy::match_single_binding)] +#![allow(clippy::to_string_in_format_args)] + use proc_macro::TokenStream; use quote::{quote, ToTokens}; use syn::{parse_macro_input, Data, DeriveInput, Fields, PathArguments, Type, TypeGroup};